Skip to content

Leaflet.js Offline — Building Maps Without Internet

DodaTech Updated 2026-06-28 3 min read

In this tutorial, you will learn about Leaflet.js Offline. We cover key concepts, practical examples, and best practices to help you master this topic.

Leaflet.js offline maps allow users to view previously cached map tiles and data when disconnected from the internet, using service workers and local storage.

What You'll Learn

By the end of this guide, you will cache map tiles with a service worker, use libraries like Leaflet.Offline for tile management, pre-download tiles for a bounding box, detect online/offline status, and build an offline-first map app.

Service Worker Caching

// sw.js
self.addEventListener('fetch', function(event) {
    if (event.request.url.includes('tile.openstreetmap.org')) {
        event.respondWith(
            caches.match(event.request)
                .then(function(cached) {
                    return cached || fetch(event.request).then(function(response) {
                        var clone = response.clone();
                        caches.open('tiles-v1').then(function(cache) {
                            cache.put(event.request, clone);
                        });
                        return response;
                    });
                })
        );
    }
});

Detecting Connectivity

window.addEventListener('online', function() {
    map._tileLayers._layers[0]._update();
    document.getElementById('status').textContent = 'Online';
});

window.addEventListener('offline', function() {
    document.getElementById('status').textContent = 'Offline mode';
});

Pre-Downloading Tiles

var bounds = [
    [40.7, -74.02],
    [40.8, -73.98]
];

var minZoom = 10;
var maxZoom = 15;
var tileUrl = 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';

for (var z = minZoom; z <= maxZoom; z++) {
    var tileBounds = L.bounds(
        map.project(bounds[0], z),
        map.project(bounds[1], z)
    );
    for (var x = tileBounds.min.x; x <= tileBounds.max.x; x++) {
        for (var y = tileBounds.min.y; y <= tileBounds.max.y; y++) {
            var url = tileUrl.replace('{z}', z).replace('{x}', x).replace('{y}', y);
            fetch(url); // Triggers SW caching
        }
    }
}

Common Mistakes

1. CORS Issues with Tile Cache

Some tile servers block CORS. Use a proxy or configure the tile server to allow caching.

2. Storage Limits

Tile caches grow quickly (a few MB per zoom level). Set a cache size limit and purge old tiles.

3. No Fallback for Uncached Tiles

When offline and a tile is missing, show a placeholder image. Do not show broken image icons.

4. Stale Tiles Not Updated

Service workers serve cached tiles indefinitely. Implement a versioning Strategy to refresh tiles.

5. GeoJSON Data Not Cached

Only tiles are cached by default. Cache API responses and GeoJSON files separately.

Practice Questions

Q1: What service worker event handles tile caching? A: The 'fetch' event intercepts tile requests and serves from cache or network.

Q2: How do you know if the user is offline? A: Listen for 'online' and 'offline' events on the window object.

Q3: What is the storage limit for a service worker cache? A: Typically 50-100MB depending on the browser. Use the Storage API to check.

Q4: How do you pre-cache tiles for a specific region? A: Iterate over the bounding box at each zoom level and fetch each tile URL.

Q5: How do you clear the tile cache? A: Use caches.open('tiles-v1').then(function(cache) { cache.keys().then(/* delete */); });

Challenge: Build an offline map app that lets users select a bounding box on the map and click "Download for Offline." Show download progress and total tiles count.

FAQ

Can I use IndexedDB for tile storage?

Yes. Libraries like idb or localForage store tiles in IndexedDB for larger storage limits.

Does Leaflet have built-in offline support?

No. Leaflet is tile-provider agnostic. Offline support must be implemented separately.

How do I use mbtiles for offline maps?

Parse the mbtiles SQLite file on the server and serve tiles via a local HTTP server.

Can I use vector tiles offline?

Yes. .mvt vector tiles can be cached like raster tiles. Render with MapLibre GL JS.

How do I handle offline GeoJSON editing?

Save edits to localStorage or IndexedDB. Sync when online.

Try It Yourself

Build an offline-aware map.

<!DOCTYPE html>
<html>
<head>
    <title>Offline Map Demo</title>
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9/dist/leaflet.css" />
    <script src="https://unpkg.com/leaflet@1.9/dist/leaflet.js"></script>
</head>
<body style="font-family:sans-serif;padding:20px;">
<h2>Offline Map</h2>
<div id="status" style="padding:6px 12px;background:#4ecdc4;color:white;border-radius:4px;margin-bottom:8px;display:inline-block;">Online</div>
<div id="map" style="height:400px;border-radius:8px;"></div>
<script>
var map = L.map('map').setView([40.7128, -74.0060], 12);

L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
    attribution: '&copy; OSM'
}).addTo(map);

var statusDiv = document.getElementById('status');

function updateStatus() {
    if (navigator.onLine) {
        statusDiv.textContent = 'Online';
        statusDiv.style.background = '#4ecdc4';
    } else {
        statusDiv.textContent = 'Offline (cached tiles only)';
        statusDiv.style.background = '#ff6b35';
    }
}

window.addEventListener('online', updateStatus);
window.addEventListener('offline', updateStatus);
updateStatus();

// Register service worker for tile caching
if ('serviceWorker' in navigator) {
    // In production, register sw.js here
    console.log('Service Worker supported');
}
</script>
</body>
</html>

What's Next

Optimize map performance.

Performance — Performance optimization. Animations — Map animations.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro