Here’s an example of how to create a Mapbox map in React using a GeoJSON data set. When I started building my first React Mapbox map I built them around some of the other pre-built components out there like ReactMapboxGL or react-map-gl. Both of these wrappers are great but eventually figured out I was better able to control all the Mapbox features on my own by targeting the Mapbox GL JS libary directly vs using a another component.
This example uses React hooks but could easily be updated for a class component if that is what you are working with. Below is one component called Districts that loads Minnesota’s eight congressional districts from a separate geojson file. After loading the districts they are given a unique fill-color and added as a layer.
You can view the entire React Mapbox GeoJSON example project in the GitHub repository or you can view a live example at clintmcmahon.github.io/react-mapbox-example
Districts.js React Component
import React, { useState, useEffect, useRef } from "react"; import mnDistricts from "./data/mn/mn-districts.geojson"; import ReactDOM from 'react-dom'; import mapboxgl from 'mapbox-gl';function Districts(props) { mapboxgl.accessToken = process.env.REACT_APP_MAPBOX_KEY; const mapContainer = useRef(null); const [long, setLong] = useState(-94.503809); const [lat, setLat] = useState(46.443226); const [zoom, setZoom] = useState(4.5); const [hoveredDistrict, _setHoveredDistrict] = useState(null); const hoveredDistrictRef = useRef(hoveredDistrict);
const setHoveredDistrict = data => { hoveredDistrictRef.current = data; _setHoveredDistrict(data); }; useEffect(() => { let map = new mapboxgl.Map({ container: mapContainer.current, style: "mapbox://styles/mapbox/light-v10", center: [long, lat], zoom: zoom }); // Add zoom and rotation controls to the map. map.addControl(new mapboxgl.NavigationControl()); map.once("load", function () { map.addSource('district-source', { 'type': 'geojson', 'data': mnDistricts }); map.addLayer({ 'id': 'district-layer', 'type': 'fill', 'source': 'district-source', 'layout': {}, 'paint': { 'fill-color': [ 'match', ['get', 'CD116FP'], '01', '#5AA5D7', '02', '#02735E', '03', '#00E0EF', '04', '#84D0D9', '05', '#202359', '06', '#CE7529', '07', '#00AE6C', '08', '#0056A3', /* other */ '#ffffff' ], 'fill-opacity': [ 'case', ['boolean', ['feature-state', 'hover'], false], .8, 0.5 ] } }); map.on('mousemove', 'district-layer', function (e) { if (e.features.length > 0) { if (hoveredDistrictRef.current && hoveredDistrictRef.current > -1) { map.setFeatureState( { source: 'district-source', id: hoveredDistrictRef.current }, { hover: false } ); } let _hoveredDistrict = e.features[0].id; map.setFeatureState( { source: 'district-source', id: _hoveredDistrict }, { hover: true } ); setHoveredDistrict(_hoveredDistrict); } }); // When the mouse leaves the state-fill layer, update the feature state of the // previously hovered feature. map.on('mouseleave', 'district-layer', function () { if (hoveredDistrictRef.current) { map.setFeatureState( { source: 'district-source', id: hoveredDistrictRef.current }, { hover: false } ); } setHoveredDistrict(null); }); }); }, []); return ( <div className="district-map-wrapper"> <div id="districtDetailMap" className="map"> <div style={{ height: "100%" }} ref={mapContainer}> </div> </div> </div> );
}
export default Districts;
Minnesota Congressional Districts GeoJSON
This geojson data set represents Minnesota’s eight congressional districts that I pulled from the US Census. Each feature has a property “CD116FP” that I’m using to set the fill-color of each district layer. The GeoJSON data is so big I didn’t include it in this blog post but you can download it from the Github repo.
You can view the source on Github.
That’s it. Happy coding!