Search code examples
javascriptreactjsnext.jsmapsmapbox

Mapbox performance issue due to large numbers of markers


I successfully added numerous interactive markers to the map that respond to clicks, but the map's performance is significantly sluggish when it comes to dragging, resulting in a low frame rate.

I'm using NextJs with ReactJs, and the markers are requsted from nextjs API route which responds in a list of objects +3k marker, example:

[
   {
      "id":"1mj080r5qtcf8",
      "name":"test",
      "number":"1024",
      "latitude":19.17022992073896,
      "longitude":1.12069176646572,
      "icon":"flower",
      "image":null
   },
   {
      "id":"1ouidhdxo8q3",
      "name":"1024",
      "number":"J36",
      "latitude":11.05615300244807,
      "longitude":1.012846731875111,
      "icon":"unknown",
      "image":null
   },
  ...more
]

Here's how I'm loading my markers in the Map react component file:

Map.tsx:



function createMarkerElement(
        icon: string,
        id?: string,
        isNew?: boolean
    ): HTMLElement {
        const iconMap: Record<string, string> = {
            flower: '/icons/flower.png',
            test: '/icons/test.png'
        };

    const iconUrl = iconMap[icon] ?? '/markers/icons/unknown.png';
    const element = document.createElement('div');

    element.style.backgroundImage = `url(${iconUrl})`;
    element.id = id ?? '';
    element.style.cursor = 'pointer';
    element.style.width = '20px';
    element.style.height = '20px';
    element.style.backgroundSize = 'cover';
    if (isNew) {
        const newElement = document.createElement('div');
        newElement.style.position = 'absolute';
        newElement.style.top = '0';
        newElement.style.right = '0';
        newElement.style.width = '5px';
        newElement.style.height = '5px';
        newElement.style.borderRadius = '50%';
        newElement.style.backgroundColor = '#00bfff';
        element.appendChild(newElement);
    }

    return element;
}



// map setup above


// markers loading:

useEffect(() => {
        try {
            axios
                .get('/api/markers/')
                .then((res) => {
                    const markersData: markerContent[] = res.data;
                    setMarkers(markersData);
                })
                .catch((err) => {
                    console.log(err);
                });
        } catch (error) {
            console.log(error);
        }
    }, []);

    useEffect(() => {
        const fetchMarkers = async () => {
            if (!map) {
                return;
            }
            markers.forEach((marker: any) => {
                const markerElement = createMarkerElement(
                    marker.icon ? marker.icon : null,
                    marker.id,
                    false
                );

 

                new mapboxgl.Marker({
                    element: markerElement,
                })
                    .setLngLat([marker.longitude, marker.latitude])
                    .addTo(map);

                containerElement.addEventListener('click', () => {
                    setSelectedMarker({
                        id: marker.id,
                        name: marker.name,
                        number: marker.number,
                        icon: marker.icon,
                        longitude: marker.longitude,
                        latitude: marker.latitude,
                    });
                });
            });
        };

        map?.on('load', fetchMarkers);
    }, [map, markers]);


return (
            <div
                id="map"
                style={{ height: '100vh', width: '100vw' }}
            />
    );
}

Solution

  • I have used mapbox layers instead of the DOM based markers elements, and it made a significant performance boost.