javascriptreactjsleafletreact-leaflet

How to use custom icons in leaflet controls?


I want to replace the default buttons in my leaflet map with custom ones, so that I can apply my own style and functionality on them. The buttons are created and placed in the map and the functions are working correctly, but somehow the icons are not rendered.

Here the code of my IconButton component:

import {useMap} from "react-leaflet";
import {useEffect} from "react";
import L from "leaflet";
import 'leaflet/dist/leaflet.css';
import locateIcon from '../assets/my-location.svg'

export default function CustomIconButton() {

    const map = useMap();

    useEffect(() => {

        const MyLocateButton = L.Control.extend({

            onAdd: () => {
                const customIconButton = L.DomUtil.create("button", 'locate-button');
                customIconButton.onclick = () => {alert('Button clicked!');};

                customIconButton.innerHTML = "<img src={locateIcon} />";

                return locateButton;
            },
        });

        const control = new MyLocateButton({position: "topleft"});
        map.addControl(control);

        return () => {
            map.removeControl(controlInstance);
        };

    }, [map]);

    return null;
}

Solution

  • this html is stringified here therefore it cannot evaluate variables inside, that is the reason you see nothing on the screen. Here "<img src={locateIcon} />"; locateIcon will never evaluate to the src you want.

    What you can do instead is create a jsx, pass the svg on the src attribute

    const ZoomInIcon = () => <img width="30" height="30" src={plusIcon} />; 
    

    and then using createRoot you can create a root to display React components inside a browser DOM node.

    const zoomInDiv = L.DomUtil.create('div', 'custom-zoom-in', container);
    createRoot(zoomInDiv).render(<ZoomInIcon />);
    

    Here is a demo to illustrate the example