Creating a dynamic “JSX” marker with react-leaflet

Nikhil John
3 min readNov 4, 2017

--

Psst: It’s actually just polled static HTML

Leaflet emerged as an alternative solution to the Google Maps API, because well — money. Google Maps costs money after a certain number of hits. Not only is it completely free, it also has a very simple declarative API and a range of plugins that cover most possible business cases.

If you’re using React, there exists a wonderful wrapper around Leaflet by Paul Le Cam called react-leaflet. I have been using react-leaflet for a while now and it’s pretty cool.

One issue that I faced though, was creating dynamic JSX icons inside markers.

A marker identifies a location on a map, and is often identified by an Icon.

The default icon from Leaflet

Leaflet provides two options for Icons — the traditional Icon for image icons and an HTML alternative, the DivIcon, if you want a lightweight alternative without images.

In more complex applications, one often ends up using the DivIcon option, as business requires more data in the icon than just an image.

In these cases, it is possible to create any icon, as long as it is HTML. But what if you want a JSX icon, say one that responds to data changes.

But what if you want a JSX icon, say one that responds to data changes?

Something like this maybe?

We are going to try and achieve exactly this, a Donut chart JSX Component, that updates according to its Props.

Before we start, here is the working Demo and GitHub Repo

Let’s start with the SVGIconComponent (stolen from here):

Nothing to see here, just a simple SVG representing a percentage arc

The syntax for defining a divIcon is:

L.divIcon({
className: 'custom icon'
html: <div>Icon content</div>
});

The html key accepts an HTML/String value. But how do we pass in a JSX component there? Certainly, you can’t directly pass it in like so:

html: <Icon perc={this.state.key} /> // Type error

Here, an unlikely hero emerges. react-dom’s renderToString method

renderToString is usually used on the server side, to render React Components as strings, and return them to clients with appropriate MIME types. But we need to do exactly that — render our component to a string!

const icon = L.divIcon({
className: 'custom-icon',
html: ReactDOMServer.renderToString(<Icon perc={this.state.key}/>) });

We now have a static representation of our JSX SVGIconComponent in our map. Yay!

But it is still static. That’s not good enough.

So how do we make this a dynamically updating element? Well, we poll for it of course.

Inside our componentDidMount lifecycle method, we force re-render our JSX component every 1 second, like so:

componentDidMount() {    
this.timer = setInterval(
() => this.setState({
key: Math.floor(Math.random() * 100) + 1
}),
1000);
}

This means that we essentially get a new static copy of our JSX component every second, which is then rendered on to the Map, and voila, we have our dynamic Icon!

Our result!

Happy coding.

--

--

Nikhil John

Tips and tricks on Travelling and Web Engineering! Senior Engineer @Microsoft