> For the complete documentation index, see [llms.txt](https://docs.mapbox.com/help/ja/llms.txt)

# Use checkboxes to toggle layers in a React app

This tutorial covers how to build a React web app that displays point data on a map with checkboxes to show and hide groups of map markers based on their properties.

Using a dataset of restaurants in Providence, Rhode Island, USA, you will create a map that allows users to toggle the visibility of different cuisine types (e.g., Italian, Mexican, Asian) using checkboxes. Each cuisine type will be represented by a different colored marker on the map.

By the end of this tutorial, you will have accomplished the following:

-   Create a map centered over Providence, Rhode Island, USA.
-   Add GeoJSON data with restaurant names and locations and cuisine type.
-   Create custom markers and color code them according to their cuisine type.
-   Add a list of checkboxes to toggle the visibility on each type of cuisine.
-   Add Interactive hover, click effects and create a popup to show restaurant details.

## Prerequisites

Before starting this tutorial, make sure you have:

-   **A Mapbox access token**: Find yours on the [Access token page](https://console.mapbox.com/account/access-tokens/) of your Developer Console.
-   **Node.js and npm.** [Download and install](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) the latest version.
-   **A Code editor**: A program like [Visual Studio Code](https://code.visualstudio.com/).
-   **Familiarity with React development.** Beginner experience with JSX, CSS, HTML and JavaScript.
-   [**Mapbox GL JS**](https://docs.mapbox.com/mapbox-gl-js/): is a JavaScript library for building web maps and applications with Mapbox.

## Set up a React app with Vite

First, set up a new React app with Vite and run the development server. You can use the Vite `npm create` command to quickly set up a React project:

```bash
$ npm create vite@latest
```

Create a React project and run the development server. Then, install Mapbox GL JS as a project dependency:

```bash
$ npm install mapbox-gl
```

For more details about which settings to use when setting up your app, see the **[Use Mapbox GL JS in a React App](https://docs.mapbox.com/help/ja/tutorials/use-mapbox-gl-js-with-react/)** tutorial. If you need more guidance, refer to that tutorial, stopping at step 3.

With your React app set up, you should see a page like the one below when you run the development server.

![Run the vite react app dev server](https://docs.mapbox.com/help/ja/assets/ideal-img/tutorials--use-mapbox-gl-js-with-react--dev-server.b3347c4.480.png)

With the development server running and Mapbox GL JS installed, you are ready to start building the app.

## Set up App state and add a Map

In this step you will set up the main `App` component to manage the application's state and render the `Map` component.

### 1. Set up App.jsx with state for cuisine categories

The `App` component will manage the application's state. Use `useState()` to create a state variable `layerState` that holds an array of objects, each representing a cuisine type with its name, color, and an `isChecked` boolean to track visibility state.

Replace the code in `App.jsx` with the following:

```javascript
import React, {useState} from 'react'
import Map from './Map'

import './App.css'

export default function App() {

  // all layers are checked by default
  const [layerState, setLayerState] = useState([
    {
      name: 'vegetarian',
      color: '#33a02c',
      isChecked: true
    },
    {
      name: 'sandwich',
      color: '#ffff99',
      isChecked: true   
    },
    {
      name: 'asian',
      color: '#6a3d9a',
      isChecked: true
    },
    {
      name: 'american',
      color: '#a6cee3',
      isChecked: true     
    },
    {
      name: 'coffee',
      color: '#e31a1c',
      isChecked: true
    },
    {
      name: 'mexican',
      color: '#cab2d6',
      isChecked: true
    },
    {
      name: 'seafood',
      color: '#1f78b4',
      isChecked: true
    },
    {
      name: 'ice cream',
      color: '#fb9a99',
      isChecked: true
    },
    {
      name: 'korean',
      color: '#cab2d6',
      isChecked: true
    },
    {
      name: 'sushi',
      color: '#b2df8a',
      isChecked: true
    },
    {
      name: 'italian',
      color: '#ff7f00',
      isChecked: true
    }
  ])

  return (
    <div className="app">
      <Map layerState={layerState} />
    </div>
  )
}

```

### 2. Add CSS for the map container

`App.jsx` imports `App.css` for basic styling. Replace the contents of `App.css` to make sure the map container and each of its ancestors fill the viewport:

```css
html, body, #root, .app, #map-container {
  height: 100%;
  width: 100%;
  margin: 0;
  padding: 0;
  font-family:Verdana, Geneva, Tahoma, sans-serif
}
```

For simplicity, all CSS styles will be added to `App.css` throughout this tutorial.

Clear any additional styles from `index.css` to avoid conflicts and light/dark mode compatibility issues:

```css
// clear this file
```

### 3. Add a Map component

`App.jsx` will also render the `Map` component, passing the `layerState` as a prop. This allows the `Map` component to access the current state of each cuisine type and control the visibility of the corresponding map layers.

Add a new component `Map.jsx`. For now, it will render a basemap centered over Providence, RI, USA. Be sure to replace `YOUR_MAPBOX_ACCESS_TOKEN` with your actual Mapbox access token from the [Mapbox console](https://console.mapbox.com).

You will add more functionality to this component in the later steps.

```javascript
import React, { useRef, useEffect, useState } from 'react'
import mapboxgl from 'mapbox-gl'

import 'mapbox-gl/dist/mapbox-gl.css'

export default function Map({ layerState }) {
  const mapContainer = useRef(null)
  const mapRef = useRef(null)

  // initializes the map when the component loads
  useEffect(() => {
    if (mapRef.current) return // init once

    // creates map instance and centers viewport over Providence, RI, USA
    mapRef.current = new mapboxgl.Map({
      accessToken: "YOUR_MAPBOX_ACCESS_TOKEN",
      container: mapContainer.current,
      center: [-71.407, 41.8205],
      zoom: 15.5
    })
  }, [])

  return (
    <div ref={mapContainer} className="map-container" />
  )
}

```

> **Note (warning): Troubleshooting**
> 
> If your map is not loading, make sure that you are `YOUR_MAPBOX_ACCESS_TOKEN` is replaced with your actual token in the script.

Once completed, your app will look like the image below, showing a blank base map centered over Providence, RI, USA:

![Center the viewport over Providence, RI, USA](https://docs.mapbox.com/help/ja/assets/ideal-img/tutorials--react-toggle-layers-viewport-providence.0df2f99.480.png)

With the Map component rendering a basemap, you are ready to add the restaurant data and create layers to visualize it.

## Add restaurant locations to the map

Next, you will add restaurant location data to the map using a GeoJSON file, and render each location with a colored marker image based on its cuisine type.

### 1. Add the GeoJSON file to your project

To add the restaurant data to your project, follow these steps:

1.  Download the geojson file, containing the restaurant locations, names and cuisine types from a set of restaurants in Providence, RI, USA:

[Download JSON](https://docs.mapbox.com/help/ja/help/data/providence-restaurants.json)

2.  Move the GeoJSON file to the `src/assets` folder.

### 2. Add the marker png to your project

To add the marker image to your project, follow these steps:

1.  Download the following image to use for your markers, or provide your own image:

[Download PNG](https://docs.mapbox.com/help/ja/help/data/custom-marker.png)

2.  Move the image you downloaded to the `src/assets` folder.

### 3. Add the restaurant data as a source and create layers

Update `Map.jsx` to import both the GeoJSON data and the marker image as shown in the first highlighted section below. Then, inside the `useEffect` that initializes the map, add the second highlighted section below to load the marker image, add the GeoJSON data as a source, and create a layer for each cuisine type.

When adding a layer for each cuisine type, common properties such as [`icon-image`](https://docs.mapbox.com/style-spec/reference/layers/#layout-symbol-icon-image), [`icon-size`](https://docs.mapbox.com/style-spec/reference/layers/#layout-symbol-icon-size), and [`icon-allow-overlap`](https://docs.mapbox.com/style-spec/reference/layers/#layout-symbol-icon-allow-overlap) are defined in the `layout` property. The `paint` property uses the color from the `layerState` to style the markers. The [`filter`](https://docs.mapbox.com/style-spec/reference/layers/#filter) property ensures that only features with the corresponding cuisine type are displayed in each layer.

Note that the image is loaded with `sdf: true` to allow for coloring the marker using the `icon-color` paint property. To learn more about using SDF images, see the [Using recolorable images in Mapbox maps](https://docs.mapbox.com/help/ja/dive-deeper/using-recolorable-images-in-mapbox-maps/) guide.

```jsx
import React, { useRef, useEffect, useState } from 'react'
import mapboxgl from 'mapbox-gl'

import 'mapbox-gl/dist/mapbox-gl.css'

// highlight-start
import restaurantsData from './assets/providence-restaurants.json'
import customMarkerPng from './assets/custom-marker.png'
// highlight-end


export default function Map({ layerState }) {
    const mapContainer = useRef(null)
    const mapRef = useRef(null)

    // initializes the map when the component loads
    useEffect(() => {
        if (mapRef.current) return // init once

        // creates map instance and centers viewport over Providence, RI, USA
        mapRef.current = new mapboxgl.Map({
            accessToken: "YOUR_MAPBOX_ACCESS_TOKEN",
            container: mapContainer.current,
            center: [-71.407, 41.8205],
            zoom: 15.5
        })

        // highlight-start
        mapRef.current.on('load', () => {

            // load image to use as a custom marker
            mapRef.current.loadImage(
                customMarkerPng,
                (error, image) => {
                    if (error) throw error;
                    mapRef.current.addImage("custom-marker", image, { sdf: true });
                }
            );

            // add a single source for all restaurants
            mapRef.current.addSource('restaurants', {
                type: 'geojson',
                data: restaurantsData
            })

            // add a layer for each cuisine type
            for (const layer of layerState) {
                const { name, color } = layer

                const layerId = `restaurants-${name}-symbol`

                if (!mapRef.current.getLayer(layerId)) {
                    // add a layer for each cuisine type, filtering to only show features with that cuisine type.
                    for (const layer of layerState) {
                        const { name, color } = layer

                        const layerId = `restaurants-${name}-symbol`

                        // add a symbol layer for each cuisine type, filtering to only show features with that cuisine type.
                        if (!mapRef.current.getLayer(layerId)) {
                            mapRef.current.addLayer({
                                id: layerId,
                                type: 'symbol',
                                source: 'restaurants',

                                // Grabs local image for custom marker, allows markers to over lap and colors each marker based on the related cuisine color.
                                layout: {
                                    'icon-image': 'custom-marker',
                                    'icon-size': 1,
                                    'icon-allow-overlap': true
                                },
                                'paint': {
                                    'icon-color': color,
                                    'icon-opacity': 0.8,
                                    'icon-halo-color': '#ffffff',
                                    'icon-halo-width': 2.5,
                                    'icon-halo-blur': 1
                                },
                                filter: ['in', ['get', 'cuisine'], ['literal', [name]]]
                            })
                        }
                    }
                }
            }
        })
        // highlight-end
    })

    return (
        <div ref={mapContainer} id="map-container" />
    )
}
```

> **Note (warning): Supported File Types**
> 
> Only `.png`, `.jpg` and `.webp` formats are supported when loading images into a style at runtime using [`map.loadImage()`](https://docs.mapbox.com/help/ja/api/map/#map#loadimage). Other file types like `.svg` are not supported.

Once finished your map should look like the image below, showing colored markers for each restaurant based on its cuisine type:

![Adds custom image markers over the POIs](https://docs.mapbox.com/help/ja/assets/ideal-img/tutorials--react-toggle-layer-symbol-layers.f648f96.480.png)

Next, you will add checkboxes to update the app state, and use the app state to toggle the visibility of each cuisine type layer.

## Add the layer checkbox component

Next, add a component to render a list of checkboxes for each cuisine type. These checkboxes will update the app state when toggled, which will in turn update the visibility of the corresponding map layers.

Add the code below to a new file `LayerCheckboxes.jsx` in the `src` folder.

```jsx

import React from 'react'

// creates a series of checkboxes to toggle layers on and off. One for each type of food and one to toggle on and off all layers.
const LayerCheckboxes = ({ layerState, setLayerState }) => {

    // Handles when a layer checkbox is toggled, turning related layers visible when toggled on and off.
    const handleCheckboxChange = (event) => {
        const { name, checked } = event.target
        setLayerState(prevLayers => {
            return prevLayers.map(layer =>
                layer.name === name ? { ...layer, isChecked: checked } : layer
            )
        })
    }

    // Handles when "All" checkbox is toggled, turning all layers on or off.
    const handleAllCheckboxChange = (event) => {
        const { checked } = event.target
        setLayerState(prevLayers => {
            return prevLayers.map(layer => ({ ...layer, isChecked: checked }))
        })
    }

    const checkedCount = layerState.filter(layer => layer.isChecked).length
    const allChecked = checkedCount === layerState.length
    const someChecked = checkedCount > 0 && checkedCount < layerState.length

    return (
        <div className="layer-checkboxes">
            {/* "All" checkbox with indeterminate state */}
            <div className="checkbox">
                <input
                    type="checkbox"
                    className="checkbox-input"
                    name="all"
                    checked={allChecked}
                    ref={input => {
                        if (input) input.indeterminate = someChecked
                    }}
                    onChange={handleAllCheckboxChange}
                />
                <label className="checkbox-label" htmlFor="all">
                    <span className="checkbox-inner" />
                    <span className="checkbox-switch" />
                </label>
                All
            </div>
            
            {layerState.map((layer) => (
                <div key={layer.name} className="checkbox">
                    <input
                        type="checkbox"
                        className="checkbox-input"
                        name={layer.name}
                        checked={layer.isChecked}
                        onChange={handleCheckboxChange}
                    />
                    <label className="checkbox-label" htmlFor={layer.name}>
                        <span className="checkbox-inner" />
                        <span className="checkbox-switch" />
                    </label>
                    <div 
                        className="color-indicator"
                        style={{
                            width: '12px',
                            height: '12px',
                            borderRadius: '50%',
                            backgroundColor: layer.isChecked ? layer.color : '#ccc',
                            marginRight: '8px'
                        }}
                    />
                    <span style={{ color: layer.isChecked ? 'inherit' : '#ccc' }}>
                        {layer.name}
                    </span>
                </div>
            ))}
        </div>
    )
}

export default LayerCheckboxes
```

Add some CSS to `App.css` to style the checkboxes and position them on the map:

```css
...

/* layer checkboxes styles  */
.checkbox {
  display: flex;
  align-items: center;
  gap: 8px;
}

.layer-checkboxes  {
  position: absolute;
  top: 12px;
  left: 12px;
  background: rgba(255,255,255,0.9);
  padding: 8px;
  border-radius: 8px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  z-index: 10;
}
.layer-checkboxes button {
  border: 1px solid #ccc;
  background: white;
  padding: 6px 8px;
  border-radius: 6px;
  cursor: pointer;
}
.layer-checkboxes button.active {
  background: #1976d2;
  color: white;
  border-color: #1565c0;
}
```

This component receives `layerState` and `setLayerState` as props from the `App` component. It renders a checkbox for each cuisine type, as well as an "All" checkbox to toggle all layers on or off. The `handleCheckboxChange` function updates the state when a checkbox is toggled, while the `handleAllCheckboxChange` function updates the state for all layers when the "All" checkbox is toggled.

Since each checkbox is a controlled component, its `checked` attribute is tied to the `isChecked` property of the corresponding layer in `layerState`. This ensures that the checkbox reflects the current visibility state of the layer. Note that inline conditional styles add a colored bullet next to each cuisine name, which is gray when the layer is unchecked.

> **Note (warning): Troubleshooting**
> 
> If you are experiencing issues with how your map and checkboxes are rendering on screen, make sure that you deleted the code from the `index.css` file.

Once added, update `App.jsx` to import and render the `LayerCheckboxes` component, passing in the `layerState` and `setLayerState` as props:

```jsx
import React, {useState} from 'react'

import Map from './Map'
// highlight-start
import LayerCheckboxes from './LayerCheckboxes'
// highlight-end

import './App.css'

export default function App() {

  ...

  return (
    <div className="app">
      <Map layerState={layerState} />
      // highlight-start
      <LayerCheckboxes layerState={layerState} setLayerState={setLayerState} />
      // highlight-end
    </div>
  )
}

```

With this step complete, you should be able to check and uncheck the boxes on screen, and see the conditional styling. Your UI component is now updating the app state, but the map layers are not yet responding to these changes. In the next step, you will connect the app state to the map layers to toggle their visibility based on the checkbox state.

![Render checkboxes on screen to match the layers](https://docs.mapbox.com/help/ja/assets/ideal-img/tutorials--react-toggle-layers-render-checkboxes.480.gif)

Next, you will update the Map component to respond to app state changes and toggle the visibility of the map layers.

## Change layer visibility based on checkbox state

With state management and the checkbox component in place, you can now connect the app state to the map layers to toggle their visibility.

To do this, you will add another `useEffect` hook in `Map.jsx` that listens for changes to the `layerState` prop. When `layerState` changes, this effect will iterate over each layer and update its visibility using `setLayoutProperty` based on the corresponding `isChecked` value.

Add the new `useEffect` from the code snippet below after the existing one that initializes the map.

```jsx
...

  useEffect(() => {
    // When layerState changes, update map visibility
    if (!mapRef.current) return // wait for map to initialize

    layerState.forEach(layer => {
      const layerId = `restaurants-${layer.name}-symbol`
      if (mapRef.current.getLayer(layerId)) {
        const visibility = layer.isChecked ? 'visible' : 'none'
        mapRef.current.setLayoutProperty(layerId, 'visibility', visibility)
      }
    })
  }, [layerState])

...

```

Once this effect is added, your map layers will respond to changes in the checkbox state. When a checkbox is checked or unchecked, the corresponding layer on the map changes its visibility.

![GIF showing the map with layers toggled on and off in response to checkbox changes](https://docs.mapbox.com/help/ja/assets/ideal-img/tutorials--react-toggle-layers-map.480.gif)

With this step complete, you have completed the core functionality of the app. You can now toggle the visibility of different cuisine types using the checkboxes, all synced with the React app state.

The next step is optional, and will enhance the user experience by adding a popup that displays additional information about a restaurant when its marker is clicked.

## Add a Popup component (Optional)

With the checkbox functionality in place, you can enhance the user experience by adding a popup that displays additional information about a restaurant when its marker is clicked.

Create a new file named `Popup.jsx` in `/src` using the code snippet below.

This component implements an instance of the [`mapboxgl.Popup`](https://docs.mapbox.com/mapbox-gl-js/api/markers/#popup) class in a React component, using `createPortal` to render JSX content into a DOM node that Mapbox GL JS can use. It will be rendered as a child of the `Map` component, and receives two props:

1.  `mapRef`: A reference to the Mapbox GL JS map instance, allowing the popup to be added to the map.
2.  `popupData`: An object containing the data for the selected restaurant, including its coordinates.

It uses a `useEffect` hook to react to changes in the `popupData` prop. When new `popupData` is provided, it sets the popup instance's coordinates and content, and adds it to the map. If `popupData` is `null`, it removes the popup instance from the map.

```jsx
// make a component that will render a popup. It will receive a prop mapRef which is a mapbox gl map instance.  It will also receive a prop called popupData which includes lngLat and properties to be rendered in the popup.
import React, { useEffect, useRef } from 'react'
import { createPortal } from 'react-dom'
import mapboxgl from 'mapbox-gl'    

export default function Popup({ mapRef, popupData }) {
  const popupRef = useRef(new mapboxgl.Popup({
    closeButton: true,
    closeOnClick: true,
    anchor: 'bottom',
    offset: [0, -25]
  }))
  const containerRef = useRef(document.createElement('div'))

  useEffect(() => {
    if (!mapRef.current) return // wait for map to initialize

    //remove the popup if there is no selected marker
    if (!popupData) {
      popupRef.current.remove()
      return
    }

    const { lngLat } = popupData

    popupRef.current
      .setLngLat(lngLat)
      .setDOMContent(containerRef.current)
      .addTo(mapRef.current)

    // cleanup function to remove popup on unmount
    return () => popupRef.current.remove()

  }, [mapRef, popupData])

  if (!popupData) return null

  const { properties } = popupData

  return createPortal(
    <div>
      <h3>{properties.name}</h3>
      <p><strong>Cuisine:</strong> {properties.cuisine}</p>
    </div>,
    containerRef.current
  )
}
```

Style the popup in `App.css` by adding the following CSS at the bottom of the file:

```css
...

.popup .cuisines { margin-top: 6px; }
.cuisine-badge {
  display: inline-block;
  background: rgba(0,0,0,0.08);
  border-radius: 12px;
  padding: 2px 8px;
  margin-right: 6px;
  font-size: 12px;
}
.cuisine-empty { color: #666; }
```

To use the `Popup` component, update `Map.jsx` to import and render it, and manage the `popupData` state. Add the highlighted sections below to `Map.jsx`.

To listen for marker clicks, loop over each object in `layerState` and add a `click` interaction using [`Map.addInteraction()`](https://docs.mapbox.com/mapbox-gl-js/api/map/#map#addinteraction). When a marker is clicked, the `popupData` state is updated with the clicked feature's data, which is passed to the `Popup` component to display the popup at the marker's location.

```jsx
import React, { useRef, useEffect, useState } from 'react'
import mapboxgl from 'mapbox-gl'

import Popup from './Popup'

import 'mapbox-gl/dist/mapbox-gl.css'

import restaurantsData from './assets/providence-restaurants.json'
import customMarkerPng from './assets/custom-marker.png'


export default function Map({ layerState }) {
    const mapContainer = useRef(null)
    const mapRef = useRef(null)

    // highlight-start
    // Manage popup visibility and the content within the popup
    const [popupData, setPopupData] = useState(null)

    // Handles when a marker is clicked, setting grabbing the marker data, passing it to a popup and then renders the over the marker.
    const handleMarkerClick = (e) => {
        setPopupData({ lngLat: e.feature.geometry.coordinates, properties: e.feature.properties });
    }
    // highlight-end

    // initializes the map when the component loads
    useEffect(() => {
        if (mapRef.current) return // init once

        // creates map instance and centers viewport over Providence, RI, USA
        mapRef.current = new mapboxgl.Map({
            accessToken: "YOUR_MAPBOX_ACCESS_TOKEN",
            container: mapContainer.current,
            center: [-71.407, 41.8205],
            zoom: 15.5
        })

        mapRef.current.on('load', () => {

            // load image to use as a custom marker
            mapRef.current.loadImage(
                customMarkerPng,
                (error, image) => {
                    if (error) throw error;
                    mapRef.current.addImage("custom-marker", image, { sdf: true });
                }
            );

            // add a single source for all restaurants
            mapRef.current.addSource('restaurants', {
                type: 'geojson',
                data: restaurantsData
            })

            // add a layer for each cuisine
            for (const layer of layerState) {
                const { name, color } = layer

                const layerId = `restaurants-${name}-symbol`

                // add a circle layer for each cuisine, filtering to only show features with that cuisine.
                if (!mapRef.current.getLayer(layerId)) {
                    // add a layer for each cuisine 
                    for (const layer of layerState) {
                        const { name, color } = layer

                        const layerId = `restaurants-${name}-symbol`

                        // add a symbol layer for each cuisine, filtering to only show features with that cuisine.
                        if (!mapRef.current.getLayer(layerId)) {
                            mapRef.current.addLayer({
                                id: layerId,
                                type: 'symbol',
                                source: 'restaurants',

                                // Grabs local image for custom marker, allows markers to over lap and colors each marker based on the related cuisine color.
                                layout: {
                                    'icon-image': 'custom-marker',
                                    'icon-size': 1,
                                    'icon-allow-overlap': true
                                },
                                'paint': {
                                    'icon-color': color,
                                    'icon-opacity': 0.8,
                                    'icon-halo-color': '#ffffff',
                                    'icon-halo-width': 2.5,
                                    'icon-halo-blur': 1
                                },
                                filter: ['in', ['get', 'cuisine'], ['literal', [name]]]
                            })
                        }
                    }
                }

                // highlight-start
                // add a click interaction for each of the layers to be used to render the popup
                mapRef.current.addInteraction(`${layerId}-click`, {
                    type: 'click',
                    target: { layerId },
                    handler: handleMarkerClick
                })
                // change the cursor to a pointer when hovering over a marker
                mapRef.current.addInteraction(`${layerId}-mouse-enter`, {
                    type: 'mouseenter',
                    target: { layerId },
                    handler: () => {
                        mapRef.current.getCanvas().style.cursor = 'pointer';
                    }
                })
                // reset the cursor to default image when cursor leaves a marker
                mapRef.current.addInteraction(`${layerId}-mouse-leave`, {
                    type: 'mouseleave',
                    target: { layerId },
                    handler: () => {
                        mapRef.current.getCanvas().style.cursor = '';
                    }
                })
                // highlight-end
            }
        })
    })

    return (
        // highlight-start
        <div ref={mapContainer} id="map-container" >
            <Popup popupData={popupData} mapRef={mapRef} />
        </div>
        // highlight-end
    )
}
```

![GIF showing popups for each restaurant when markers are clicked](https://docs.mapbox.com/help/ja/assets/ideal-img/tutorials--react-toggle-layers-popup.480.gif)

## Finished product

After adding a popup, you've built a full-featured map that sources restaurant data from a local geojson file, adds custom markers on a symbol layer that color according to the POI's cuisine type, and added checkboxes that toggle layer visibility so a user can choose which types of restaurants to display.

## Next steps

### What we covered

-   How to create a React app with symbol layers based on geojson data
-   How to implement custom markers and a popup
-   How to create checkboxes to toggle layer visibility
-   How to handle map interactions and state updates

### Things to try

Here are some ideas for improving your application:

-   **Configure the basemap**: Add `config` parameters to customize the appearance of the Mapbox Standard Style, used as the basemap in this tutorial. Explore configuration options in the [Mapbox Standard Style Playground](https://docs.mapbox.com/playground/standard-style/).
-   **Add custom marker images**: Instead of a single SDF icon colored via the `icon-color` property, use different images for each cuisine.
-   **Add your own data**: Swap out the restaurant data for your own point data in GeoJSON format.
-   **Use a vector tileset**: If you have a large dataset, consider using [Mapbox Tiling Service](https://docs.mapbox.com/mapbox-tiling-service/guides/) to create a vector tileset from your data, and load it into your map as a vector source instead of using the a GeoJSON source.

### Learn more

To learn more, read these related examples and resources:

-   [Create a custom style](https://docs.mapbox.com/help/ja/help/tutorials/create-a-custom-style/)
-   [Clustering with Mapbox Tiling Services](https://docs.mapbox.com/help/ja/help/tutorials/cluster-point-data-with-mts/)

The full code for this tutorial is available in the Mapbox [Tutorials Repository](https://github.com/mapbox/tutorials/tree/toggle-layers-react/react-toggle-layers) on GitHub.