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!