React + Mapbox GeoJSON Example

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!