Progressively enhancing maps
The Session has been online for over 20 years. When you maintain a site for that long, you don���t want to be relying on third parties���it���s only a matter of time until they���re no longer around.
Some third party APIs are unavoidable. The Session has maps for sessions and other events. When people add a new entry, they provide the address but then I need to get the latitude and longitude. So I have to use a third-party geocoding API.
My code is like a lesson in paranoia: I���ve built in the option to switch between multiple geocoding providers. When one of them inevitably starts enshittifying their service, I can quickly move on to another. It���s like having a ���go bag��� for geocoding.
Things are better on the client side. I���m using other people���s JavaScript libraries���like the brilliant abcjs���but at least I can self-host them.
I���m using Leaflet for embedding maps. It���s a great little library built on top of Open Street Map data.
A little while back I linked to a new project called OpenFreeMap. It���s a mapping provider where you even have the option of hosting the tiles yourself!
For now, I���m not self-hosting my map tiles (yet!), but I did want to switch to OpenFreeMap���s tiles. They���re vector-based rather than bitmap, so they���re lovely and crisp.
But there���s an issue.
I can use OpenFreeMap with Leaflet, but to do that I also have to use the MapLibre GL library. But whereas Leaflet is 148K of JavaScript, MapLibre GL is 800K! Yowzers!
That���s mahoosive by the standards of The Session���s performance budget. I���m not sure the loveliness of the vector maps is worth increasing the JavaScript payload by so much.
But this doesn���t have to be an either/or decision. I can use progressive enhancement to get the best of both worlds.
If you land straight on a map page on The Session for the first time, you���ll get the old-fashioned bitmap map tiles. There���s no MapLibre code.
But if you browse around The Session and then arrive on a map page, you���ll get the lovely vector maps.
Here���s what���s happening���
The maps are embedded using an HTML web component called embed-map. The fallback is a static image between the opening and closing tags. The web component then loads up Leaflet.
Here���s where the enhancement comes in. When the web component is initiated (in its connectedCallback method), it uses the Cache API to see if MapLibre has been stored in a cache. If it has, it loads that library:
caches.match('/path/to/maplibre-gl.js').then( responseFromCache => { if (responseFromCache) { // load maplibre-gl.js }});Then when it comes to drawing the map, I can check for the existence of the maplibreGL object. If it exists, I can use OpenFreeMap tiles. Otherwise I use the old Leaflet tiles.
But how does the MapLibre library end up in a cache? That���s thanks to the service worker script.
During the service worker���s install event, I give it a list of static files to cache: CSS, JavaScript, and so on. That includes third-party libraries like abcjs, Leaflet, and now MapLibre GL.
Crucially this caching happens off the main thread. It happens in the background and it won���t slow down the loading of whatever page is currently being displayed.
That���s it. If the service worker installation works as planned, you���ll get the nice new vector maps. If anything goes wrong, you���ll get the older version.
By the way, it���s always a good idea to use a service worker and the Cache API to store your JavaScript files. As you know, JavaScript is unduly expensive to performance; not only does the JavaScript file have to be downloaded, it then has to be parsed and compiled. But JavaScript stored in a cache during a service worker���s install event is already parsed and compiled.
Jeremy Keith's Blog
- Jeremy Keith's profile
- 55 followers
