Adding tile caching to maps in React-Leaflet

While writing a toy project that heavily relies on maps, I chose Leaflet as the map rendering library. From their site:

Leaflet is the leading open-source JavaScript library for mobile-friendly interactive maps. Weighing just about 38 KB of JS, it has all the mapping features most developers ever need.

The ease of use and ability to use different tile servers is the largest draw to me, however another major factor is the ability to cache tiles. A “tile” is a single “square” of the map, and is typically sent to the browser from a tile server whenever the map is moved or zoomed. Caching obviously makes this process a lot faster.

YagaJS already wrote a custom Leaflet layer which caches already requested tiles in the browser: YAGA cached Tile-Layer for Leaflet. However this was written before React-Leaflet entered the scene. React-Leaflet is a library which abstracts Leaflet into ReactJS components, making it very easy to integrate Leaflet into your React projects.

This is what I needed to do to integrate the cached tile-layer into React-Leaflet:

Instantiate a new CachedTileLayer within “LeafletConsumer”

The getting started instructions for the cached tile-layer has a straightforward example:

const CachedTileLayer = require('@yaga/leaflet-cached-tile-layer').CachedTileLayer;
const Map = require('leaflet').Map;

...

const map = new Map('map').setView([51.505, -0.09], 13);

const leafletCachedTileLayer = new CachedTileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
        attribution: '<a href="http://osm.org/copyright">OpenStreetMap</a> contributors',
        databaseName: 'tile-cache-data', // optional
        databaseVersion: 1, // optional
        objectStoreName: 'OSM', // optional
        crawlDelay: 500, // optional
        maxAge: 1000 * 60 * 60 * 24 * 7 // optional
    }).addTo(map);

However, in the last line note the layer requires access to the Leaflet “Map” object, which is not exposed directly when using React Leaflet. Instead, React Leaflet exposes the “Map” object within a “LeafletContext” HoC, as outlined in their documentation. So the above layer instantiation must happen within this LeafletContext, like so:

//taken from: https://github.com/dvas0004/tallinn-transport-gps/blob/master/src/components/MapCacheLayer.tsx

import React from 'react';
import { LeafletConsumer } from 'react-leaflet';
import { CachedTileLayer } from '@yaga/leaflet-cached-tile-layer';

const MapCacheLayer= () =>{
    
    console.log("Adding caching")
    return <LeafletConsumer>
        {
            context => {
                new CachedTileLayer("https://{s}.tile.osm.org/{z}/{x}/{y}.png", {
                    attribution: `<a href="http://osm.org/copyright">OpenStreetMap</a> contributors`,
                    databaseName: "tile-cache-data", // optional
                    databaseVersion: 1, // optional
                    objectStoreName: "OSM", // optional
                    crawlDelay: 500, // optional
                    maxAge: 1000 * 60 * 60 * 24 * 7, // optional
                }).addTo(context.map)
                return <div />
            }
        }
    </LeafletConsumer>

}

export default MapCacheLayer;

The above defines a new React component “<MapCacheLayer />” which uses the LeafletConsumer to get access to the “Map” object which the tile caching layer needs.

Use this component within <Map />

As instructed in the React Leaflet documentation, you can instantiate a Leaflet map by simply using <Map />. With the above component, it’s now a simple matter of adding the component as a child of this Map component, for example:

//taken from: https://github.com/dvas0004/tallinn-transport-gps/blob/master/src/components/MapComponent.tsx
<Map center={[0,0]} zoom={16}>        
  <TileLayer 
     attribution='...'
     url='https://{s}.tile.osm.org/{z}/{x}/{y}.png'
  />
  <MapCacheLayer />  
</Map>