This guide will teach you how to add popups to a web map using React and Mapbox GL JS. Introduction. If I am building a map for a client, one of ...
HomeAboutWritingReadingAboutIliketowritealmostasmuchasIliketobuild.Iwriteaboutdashboards,maps,freelancing,music,andtheoutdoors.Ifthisinterestsyoutoo,pleasejoinmymailinglisttogetupdateswheneverIpublishnewcontent.SubscribeIliketowritealmostasmuchasIliketobuild.Iwriteaboutdashboards,maps,freelancing,music,andtheoutdoors.Ifthisinterestsyoutoo,pleasejoinmymailinglisttogetupdateswheneverIpublishnewcontent.SubscribeHowtoCreateaMapPopupComponentUsingMapboxandReactDate:4/25/2021TimetoRead:5minutes
ThispostispartofmyBuildingInteractiveMapswithReactcourse-acourseforanyonewantingtolearnhowtobuildinteractivemapsandintegratethemintotheirReactapplications.Ifyouenjoythisguide,thenchancesareyouwillenjoythecoursetoo!WhatYouWillLearnInteractivityispartofwhatmakeswebmapssocompelling.Arelativelyeasywaytoaddinteractivitytoawebmapisbyaddingpopupstodisplayinformationaboutyourmaplayers.ThisguidewillteachyouhowtoaddpopupstoawebmapusingReactandMapboxGLJS.IntroductionIfIambuildingamapforaclient,oneofthefirstthingstheytypicallyaskis"Canithavepopups?"Assuch,understandinghowtoquicklybuildandstylemappopupshasbecomeanessentialpartofmydevelopmentworkflow.LuckilytheprocessisrelativelystraightforwardandReactandMapboxGLJSplayverynicelytogether.Forthisguide,wewillbebuildingasimplemapshowcasingbusroutesforSaltLakeCity,Utahandcreatingasimplemappopupthatshowssomeinfoaboutaroutewhenauserclicksonit.GettingStartedThisguideassumesyouhavemovedthroughtheotherguidesthatarepartofthiscourseandarenowcomfortablecreatinganinteractivemapusingMapboxGLJSandReact.Foreaseofuse,wearegoingtouseCreateReactApptogetourapplicationupandrunning.Ifyouwouldliketofollowalong,IhaveputtogetherbothaCodeSandboxandanexamplerepositorywhichyoucanfindhere.Tobegin,createanewapplicationusingCreateReactAppandnavigateintotheproject.1npxcreate-react-appmapbox-and-popups2cdmapbox-and-popups3
Intherootoftheproject,createanew.envfileandcopyyourMapboxaccesstokenfromyouraccountpage.Ifyouneedarefresheronhowtogetyouraccesstoken,checkouttheIntroductiontoMapboxandReactguide.Thencreateanewentryinthe.envfiletitledREACT_APP_MAPBOX_TOKENandsetitsvalueequaltotheaccesstokenyoucopiedtoyourclipboard.Itisgenerallybestpracticetostoresensitiveinformationlikeaccesstokensina.envfileandkeepthemoutofversioncontrol.1REACT_APP_MAPBOX_TOKEN=2
Next,weneedtoaddtheMapboxGLJSlibrarytoourprojectasadependency.1#yarn2yarnaddmapbox-gl3
4#npm5npminstallmapbox-gl6
SettingUptheMapAsmentionedearlier,wewillbecreatinganinteractivemapthatshowsbusroutesinSaltLakeCityanddisplaysapopupwithsomesomeinformationonthebusroutewhentheuserclicksonaroute.Thistutorialassumesyoualreadyknowhowtosetupabasicmapandaddasourceandlayerstoit.Ifyouarenotfamiliarwiththisworkflow,pleasereviewtheIntroductiontoMapboxandReactguideandtheACompleteGuidetoSourcesandLayersinReactandMapboxGLJSguidebeforecontinuing.Forthesakeofsimplicity,IwillbesettingupthemapintherootofthesrcdirectoryintheApp.jsfile.Thefollowingsnippetdoesthefollowing:createsabasicinteractivemapaddsaspatialdatasourceforthebusroutesusinganESRIAPIaddsalinelayertothemapforvisualizingthebusroutes1importReact,{useRef,useEffect}from"react"2importReactDOMfrom"react-dom"3importmapboxglfrom"mapbox-gl"4//importthemapboxstyles5//alternativelycanusealinktagintheheadofpublic/index.html6//seehttps://docs.mapbox.com/mapbox-gl-js/api/7import"mapbox-gl/dist/mapbox-gl.css"8import"./App.css"9
10//GrabtheaccesstokenfromyourMapboxaccount11//Itypicallyliketostoresensitivethingslikethis12//ina.envfile13mapboxgl.accessToken=process.env.REACT_APP_MAPBOX_TOKEN14
15constApp=()=>{16constmapContainer=useRef()17
18//thisiswhereallofourmaplogicisgoingtolive19//addingtheemptydependencyarrayensuresthatthemap20//isonlyrenderedonce21useEffect(()=>{22//createthemapandconfigureit23//checkouttheAPIreferenceformoreoptions24//https://docs.mapbox.com/mapbox-gl-js/api/map/25constmap=newmapboxgl.Map({26container:mapContainer.current,27style:"mapbox://styles/mapbox/outdoors-v11",28center:[-111.94,40.611],29zoom:12,30})31
32//onlywanttoworkwiththemapafterithasfullyloaded33//ifyoutrytoaddsourcesandlayersbeforethemaphasloaded34//thingswillnotworkproperly35map.on("load",()=>{36//busroutessource37//anotherexampleofusingageojsonsource38//thistimewearehittinganESRIAPIthatreturns39//datainthegeojsonformat40//seehttps://docs.mapbox.com/mapbox-gl-js/style-spec/sources/#geojson41map.addSource("bus-routes",{42type:"geojson",43data:44"https://opendata.arcgis.com/datasets/4347f3565fbe4d5dbb97b016768b8907_0.geojson",45})46
47//busroutes-linelayer48//seehttps://docs.mapbox.com/mapbox-gl-js/style-spec/layers/#line49map.addLayer({50id:"bus-routes-line",51type:"line",52source:"bus-routes",53paint:{54"line-color":"#4094ae",55"line-width":4,56},57})58})59
60//cleanupfunctiontoremovemaponunmount61return()=>map.remove()62},[])63
64return65}66
67exportdefaultApp68
DecidingWhattoShowinthePopupNowthatwehavethebusroutesdisplayingonourmap,itistimetoaddsomeinteractivity.Thegoalistodisplaysomeinformationaboutabusroutewhenauserclicksontherouteonthemap.ToaccomplishthiswewillbecreatingaPopupcomponentandthenaddingsomelogictorenderthePopupcomponentwithinformationabouttheroutetheuserclickedon.Eachbusroutehassomebasicmetadata.Thesnippetbelowhighlightsthetypeofdatathatisavailableforeachroute.1{2"FID":96,3"LineAbbr":"F578",4"LineName":"7800SFLEX",5"Frequency":"30",6"RouteType":"Flex",7"City":"Midvale,WestJordan",8"County":"SaltLake",9"AvgBrd":101,10"SHAPE_Length":0.1434644445022264511}12
TheinfothatislikelymostrelevantfortheuseraretheLineAbbr,LineName,RouteType,andCityproperties.Accordingly,wewillsetupthemappopuptodisplaythedataforthesefields.CreatingthePopupNowthatwehavedecidedwhatinformationwewouldliketohighlight,wecangoaboutcreatingourPopupcomponent.Foreaseofdemonstration,IamgoingtocreatethePopupcomponentinourexistingApp.jsfileversusbreakingthecomponentoutintoitsownfile.HereisthefullsnippetforcreatingthePopupcomponentandgettingitwireduptothemap.Ifyouarejustlookingforasnippet,feelfreetocopyandpasteandgoonyourway.Otherwise,Iwillcontinuewithadetailedexplanationbelow.1importReact,{useRef,useEffect}from"react"2importReactDOMfrom"react-dom"3importmapboxglfrom"mapbox-gl"4//importthemapboxstyles5//alternativelycanusealinktagintheheadofpublic/index.html6//seehttps://docs.mapbox.com/mapbox-gl-js/api/7import"mapbox-gl/dist/mapbox-gl.css"8import"./App.css"9
10//GrabtheaccesstokenfromyourMapboxaccount11//Itypicallyliketostoresensitivethingslikethis12//ina.envfile13mapboxgl.accessToken=process.env.REACT_APP_MAPBOX_TOKEN14
15/**16*OurcustomPopupcomponentusedtorenderanicelystyled17*mappopupwithadditionalinformationaboutthe18*user'sselectedbusroute19*/20constPopup=({routeName,routeNumber,city,type})=>(2122{routeName}2324Route#25{routeNumber}262728RouteType29{type}3031Serves{city}
3233)34
35constApp=()=>{36constmapContainer=useRef()37constpopUpRef=useRef(newmapboxgl.Popup({offset:15}))38
39//thisiswhereallofourmaplogicisgoingtolive40//addingtheemptydependencyarrayensuresthatthemap41//isonlyrenderedonce42useEffect(()=>{43//createthemapandconfigureit44//checkouttheAPIreferenceformoreoptions45//https://docs.mapbox.com/mapbox-gl-js/api/map/46constmap=newmapboxgl.Map({47container:mapContainer.current,48style:"mapbox://styles/mapbox/outdoors-v11",49center:[-111.94,40.611],50zoom:12,51})52
53//onlywanttoworkwiththemapafterithasfullyloaded54//ifyoutrytoaddsourcesandlayersbeforethemaphasloaded55//thingswillnotworkproperly56map.on("load",()=>{57//busroutessource58//anotherexampleofusingageojsonsource59//thistimewearehittinganESRIAPIthatreturns60//datainthegeojsonformat61//seehttps://docs.mapbox.com/mapbox-gl-js/style-spec/sources/#geojson62map.addSource("bus-routes",{63type:"geojson",64data:65"https://opendata.arcgis.com/datasets/4347f3565fbe4d5dbb97b016768b8907_0.geojson",66})67
68//busroutes-linelayer69//seehttps://docs.mapbox.com/mapbox-gl-js/style-spec/layers/#line70map.addLayer({71id:"bus-routes-line",72type:"line",73source:"bus-routes",74paint:{75"line-color":"#4094ae",76"line-width":4,77},78})79})80
81/**82*Eventhandlerfordefiningwhathappenswhenauserclicksonthemap83*Inthisexample,wearecheckingiftheuserhasclickedonabusroute84*Iftheyhave,wewanttorenderapopupwiththedatafortheselected85*busroute86*Else,donothing87*/88map.on("click",e=>{89constfeatures=map.queryRenderedFeatures(e.point,{90layers:["bus-routes-line"],91})92if(features.length>0){93constfeature=features[0]94//createpopupnode95constpopupNode=document.createElement("div")96ReactDOM.render(97
,103popupNode104)105popUpRef.current106.setLngLat(e.lngLat)107.setDOMContent(popupNode)108.addTo(map)109}110})111
112//cleanupfunctiontoremovemaponunmount113return()=>map.remove()114},[])115
116return
117}118
119exportdefaultApp120
ThePopupComponentLet'sstartwiththePopupcomponent.1/**2*OurcustomPopupcomponentusedtorenderanicelystyled3*mappopupwithadditionalinformationaboutthe4*user'sselectedbusroute5*/6constPopup=({routeName,routeNumber,city,type})=>(7
8{routeName}910Route#11{routeNumber}121314RouteType15{type}1617Serves{city}1819)20
ThecomponentissupersimpleandonlyexpectstobepassedpropsfortherouteName,routeNumber,city,andtype.WethenwritesomebasicJSXtocontrolhowtheprovidedinformationisstructuredandthenaddclassnamestocontroltheinformationisstyled.BecauseweareleveragingtheMapboxGLJSPopupclass,wereallydonothavedomuchintermsofstylingandfunctionalitytogetthepopupdisplayingasdesired.Mapboxhandlesallofthehardstufflikethepopuppositioningandbasestylesforus.ThebasepopupstylesareprettyboringthoughsoIdecidedtosprucethemupabitforthisguide.HerearethecssrulesthatarebeingloadedinfromanApp.cssfileinmyapplication.1.mapboxgl-popup-content{2min-width:200px;3}4
5.route-metric-row{6align-items:center;7background-color:#fafafa;8border:1pxsolid#ddd;9border-radius:16px;10display:flex;11justify-content:space-between;12margin:8px0;13padding-right:4px12px;14}15
16.row-title{17align-items:center;18background-color:#4094ae;19border:1pxsolid#4094ae;20border-radius:16px;21color:#ffffff;22font-weight:normal;23justify-content:center;24margin:0;25padding:4px12px;26text-align:center;27}28
29.row-value{30color:rgba(0,0,0,0.7);31font-size:0.875rem;32padding-right:16px;33text-align:right;34}35
36.route-name{37color:rgba(0,0,0,0.7);38font-size:0.875rem;39margin:0;40}41
42.route-city{43background-color:#fafafa;44border:1pxsolid#ddd;45border-radius:4px;46color:rgba(0,0,0,0.65);47font-size:0.8rem;48margin-bottom:0;49padding:8px;50}51
CreatingaPopupInstanceThenextstepinthisprocessisinitializinganewMapboxGLJSpopupinstanceandstoringitinaref.ThePopupconstructorcantakeavarietyofconfigurationoptions.Forourpurposes,weareleveragingtheoffsetoptiontoensurethatthepopuprenders15pxawayfromthefeaturetheuserhasclickedon.ForacompletelistoftheavailableconfigurationoptionspleasecheckouttheMapboxPopupdocs.1constpopUpRef=useRef(newmapboxgl.Popup({offset:15}))2
RenderingthePopupWhentheUserClicksonaRouteAftercreatingourPopupcomponentandanewinstanceoftheMapboxPopupclass,weneedtoaddlogictoourmapthattellsMapboxtodisplaythePopupcomponentwhenauserclicksonabusroute.Webeginbyaddinganeventhandlerforwhentheuserclicksonthemap.Youcanimaginethelaundrylistofthingsyoucoulddowhenauserclicksonthemap,butforourpurposeswewanttofocusourattentionondetectingiftheuserhasclickedonthebusrouteslayer.MapboxprovidesahandymethodontheMapclasscalledqueryRenderedFeaturesthatallowsyoutoquerythemapforanymapfeaturesthatexistataprovidedlatitudeandlongitude.Whenauserclicksonthemap,wegetaspecialMapboxeventobjectthatcontainsalatitudeandlongitudeamongotherthingsthat,wecaneasilypasstothequeryRenderedFeaturesmethodtodetermineiftheuserhasclickedonthebusrouteslayers.ThequeryRenderedFeaturesmethodalsotakesanoptionaloptionsobjectthatwewillleveragetoonlyquerythemapforfeaturesinthebus-routes-linelayer.YoucancheckouttheMapboxdocsformoreinformationonthemethod.Iftheuserhasclickedonabusroute,thequeryRenderedFeaturesmethodwillreturnanarrayoffeatures,otherwiseitwillreturnanemptyarray.Iftheuserdidindeedclickonafeature,wethenwanttoexecuteablockoflogictocreateanewdiv,renderthePopupcomponenttoit,andaddittothemapatthelatitudeandlongitudewheretheuserclickedthemap.1/**2*Eventhandlerfordefiningwhathappenswhenauserclicksonthemap3*Inthisexample,wearecheckingiftheuserhasclickedonabusroute4*Iftheyhave,wewanttorenderapopupwiththedatafortheselected5*busroute6*Else,donothing7*/8map.on("click",e=>{9constfeatures=map.queryRenderedFeatures(e.point,{10layers:["bus-routes-line"],11})12if(features.length>0){13constfeature=features[0]14//createpopupnode15constpopupNode=document.createElement("div")16ReactDOM.render(17,23popupNode24)25popUpRef.current26.setLngLat(e.lngLat)27.setDOMContent(popupNode)28.addTo(map)29}30})31
NextStepsThisallmayseemlikealotinitially(atleastitwasforme),butitbecomesaneasierdesignpatternthatyouwillgetusedtolike.ThePopupcomponentwebuiltinthisguideisrelativelysimplebutReactmakesitpossibletobuildsomeverycomplexandpowerfulpopupswillarelativelylowamountofeffort.Forinstance,Ihavehadtoaddimages,links,andeventchartstopopupsandeachoftheseusecaseswasarelativelyminorlift.Ifyouarehungeringformoreafterthisguide,Irecommendtryingoneoftheseusecasesnext!Ifyoufoundthuspostuseful,givemeafollowonTwitterorconsiderpickingupacopyoftheBuildingInteractiveMapswithReactcourse.UsefulResourcesExampleGithubRepoExampleCodeSandboxMapboxPopupdocsMapbox'squeryRenderedFeaturesmethodAddpointstoawebmap,part3:addinteractivityMapboxReactExamples:ReactTooltipJointheNewsletterIperiodicallysendoutanewsletter.Interested?Signupbelow.Youcanunsubscribeatanytime.Interestedinworkingtogether?Dropmealine