La Tinaja

Building a Gatsby Site with Leaflet Maps

20 Jun 2019

Recently I was working on a custom touring map for an article on Local Memory. The site is built with Gatsby, and I use Leaflet for mapping. I use React-Leaflet for some convenience with treating the Leaflet maps as React components.

The problem comes when running gatsby build. In the Node.js environment, there is no window object, and Webpack fails to build the first page it encounters where a React-Leaflet Map component is rendered.

The fix is to make a check against the window object in the page’s render() function before using a Map component:

render() {
  if (typeof window !== 'undefined') {
    return (
      <div ref={this.mapWrapperRef} className="map-wrapper">
         <Map ref={this.mapRef}
            style={{height: '540px'}}
          >
          <TileLayer
            url="https://tiles.wmflabs.org/bw-mapnik/{z}/{x}/{y}.png"
            attribution='&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
          />
        </Map>
      </div>
    );
  }
  
  return null;
}

This was enough to avoid the Webpack error, but the problem was that the map would intermittently fail to display in the built version.

The problem was returning null from the render() function which meant that there was not an element in the DOM to mount the map on when it was ready. Outputting an empty wrapper <div> rather than nothing at all from the render() function when not in a browser environment satisfies both conditions.

render() {
  return (
    <div ref={this.mapWrapperRef} className="map-wrapper">
      {(typeof window !== 'undefined') ? (
         <Map ref={this.mapRef}
            style={{height: '540px'}}
          >
          <TileLayer
            url="https://tiles.wmflabs.org/bw-mapnik/{z}/{x}/{y}.png"
            attribution='&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
          />
        </Map>
      ) : null}
    </div>
  );
}

A related Webpack related build error can be fixed with the following in your gatsby-node.js so that the build doesn’t fail on pages that import react-leaflet because of the absence of a browser environment:

exports.onCreateWebpackConfig = ({ stage, rules, loaders, actions }) => {
  switch (stage) {
    case 'build-html':
      actions.setWebpackConfig({
        module: {
          rules: [
            {
              test: /react-leaflet/,
              use: [loaders.null()]
            }
          ]
        }
      });
      break;
  }
};