GeoLibre PWA Caching Strategies for Offline Functionality

GeoLibre implements a three-layer caching architecture using vite-plugin-pwa and Workbox, combining app-shell precaching with runtime CacheFirst rules to enable fully offline geospatial operations without directly writing to the Cache Storage API.

GeoLibre, developed by the opengeos organization, delivers desktop-grade geospatial capabilities through a Progressive Web App (PWA) architecture that functions entirely offline. The application's caching system is orchestrated through a service worker generated by vite-plugin-pwa, which manages three distinct storage layers: the application shell, computational assets, and basemap tiles.

App-Shell Precaching Strategy

The first layer ensures the user interface renders instantly upon loading. During the service worker installation phase, GeoLibre precaches the essential bootstrap resources required to display the application shell.

In apps/geolibre-desktop/vite.config.ts, the VitePWA plugin configuration defines glob patterns that capture the HTML entry point, static JavaScript and CSS bundles, fonts, and UI images. These assets are revision-hashed and stored using Workbox's precaching mechanism, ensuring users always receive the latest version of the application shell while maintaining offline availability.

// vite.config.ts - Precache configuration excerpt
VitePWA({
  workbox: {
    globPatterns: ['**/*.{html,js,css,woff2,png,svg}'],
    // Additional precache options...
  }
})

Runtime Caching for Assets and Engines

The second layer handles large, lazily-loaded computational resources using a CacheFirst strategy. This approach stores heavy assets after their first network fetch, serving subsequent requests directly from the cache.

The configuration in apps/geolibre-desktop/vite.config.ts (lines 96-135) defines two specific runtime caching rules:

  • geolibre-assets: Caches bundled chunks from /assets/ and WebAssembly blobs
  • geolibre-cdn-engines: Caches external binaries including Pyodide, PGlite, CereusDB, and GDAL distributions

Both rules implement expiration policies limiting storage to 30 days or 300–400 entries, preventing cache bloat while maintaining offline functionality for essential computational engines.

// vite.config.ts - Runtime caching for assets
{
  urlPattern: /^https:\/\/.*\/assets\/.*/i,
  handler: 'CacheFirst',
  options: {
    cacheName: 'geolibre-assets',
    expiration: {
      maxEntries: 400,
      maxAgeSeconds: 60 * 60 * 24 * 30 // 30 days
    }
  }
}

Runtime Caching for Basemap Tiles

The third layer manages dynamic geospatial visualization data through CacheFirst rules targeting CORS-friendly basemap providers. This strategy enables offline map viewing by storing vector tiles, raster tiles, style JSON, sprites, and glyphs.

Configured in apps/geolibre-desktop/vite.config.ts (lines 140-158), the geolibre-basemaps cache captures all requests to openfreemap.org and cartocdn.com with generous limits of 8,000 entries and 30-day expiration. The service worker matches these URLs using hostname regex patterns, allowing any region downloaded by the user to persist for offline use.

// vite.config.ts - Basemap tiles caching rule
{
  urlPattern: ({ url }) =>
    /(?:^|\.) (?:openfreemap\.org|cartocdn\.com)$/.test(url.hostname),
  handler: 'CacheFirst',
  options: {
    cacheName: 'geolibre-basemaps',
    expiration: {
      maxEntries: 8000,
      maxAgeSeconds: 60 * 60 * 24 * 30
    },
    cacheableResponse: { statuses: [0, 200] }
  }
}

Offline Region Download Workflow

GeoLibre implements an indirect caching mechanism that deliberately avoids direct Cache Storage API manipulation. Instead, the application uses standard fetch() requests that pass through the service worker, allowing Workbox to handle storage according to its predefined rules.

This architecture is implemented in apps/geolibre-desktop/src/lib/offline-tiles.ts. When users select a geographic region for offline use, the system enumerates every required tile URL—including style assets and glyphs—and issues ordinary fetch requests. The service worker intercepts these requests and stores responses via the geolibre-basemaps CacheFirst route.

The core functions driving this process include:

  • planOfflineZoom - Resolves the zoom range for download, respecting the map's maximum zoom constraints
  • collectOfflineUrls - Gathers tile-template URLs and style assets, deduplicating entries
  • warmUrls - Performs bounded-concurrency fetches (default 8 concurrent requests) with progress reporting and failure collection
  • hasActiveServiceWorker - Verifies service worker control before initiating downloads
// offline-tiles.ts - Cache warming implementation
export async function warmUrls(
  urls: string[],
  options: {
    concurrency?: number;
    timeoutMs?: number;
    onProgress?: (progress: WarmProgress) => void;
  }
) {
  const { concurrency = 8, timeoutMs = 15000, onProgress } = options;
  
  // Fetch implementation that passes through service worker
  const fetchWithTimeout = (url: string) => 
    Promise.race([
      fetch(url),
      new Promise((_, reject) => 
        setTimeout(() => reject(new Error('Timeout')), timeoutMs)
      )
    ]);

  // Process with bounded concurrency and progress tracking...
}

Service Worker Activation and Testing

GeoLibre configures the service worker with injectRegister: false to enable manual registration control, ensuring the application determines when caching behavior initiates. The worker is configured with clientsClaim: true and skip-waiting behavior to immediately assume control of pages without requiring multiple reloads.

The end-to-end test suite in e2e/pwa.spec.ts validates the complete offline architecture. Tests verify that after initial page load, the service worker activates correctly, caches map assets, and successfully boots the application during subsequent offline reloads.

Key Source Files

Summary

GeoLibre's PWA offline functionality relies on a sophisticated three-tier caching system:

  • Precached App Shell - HTML, JavaScript, CSS, and fonts stored at install time for instant loading
  • Asset Caching - CacheFirst strategy for WASM binaries and engine files with 30-day expiration
  • Basemap Caching - CacheFirst strategy for tiles with 8,000 entry capacity, populated indirectly through fetch requests
  • Indirect Storage - Region downloads use standard fetch calls rather than direct Cache API writes, allowing Workbox to manage all cache entries
  • Manual SW Control - Custom registration and immediate client claiming ensure offline availability after first visit

Frequently Asked Questions

How does GeoLibre store map tiles for offline use without using the Cache Storage API directly?

GeoLibre employs an indirect caching mechanism where the offline-tiles.ts module enumerates all required tile URLs for a selected region and issues standard fetch() requests. These requests pass through the Workbox-generated service worker, which applies the geolibre-basemaps CacheFirst strategy to store responses. This design ensures the generated service worker remains the sole cache writer, maintaining consistency with the runtime caching rules defined in vite.config.ts.

What is the maximum storage duration for offline basemap tiles?

The basemap tile cache uses a 30-day expiration policy with a maximum of 8,000 entries, as configured in the geolibre-basemaps runtime caching rule. This applies to tiles from openfreemap.org and cartocdn.com, ensuring downloaded regions remain available for offline viewing for one month before automatic eviction.

How does GeoLibre handle large computational assets like Pyodide and GDAL for offline use?

Large engine binaries and WASM files are cached using separate CacheFirst runtime routes named geolibre-assets and geolibre-cdn-engines. These routes store files after the first network request with expiration limits of 30 days or 300–400 entries, allowing complex geospatial processing to function offline after initial load.

Why does GeoLibre require an active service worker before allowing offline downloads?

The hasActiveServiceWorker() check ensures the service worker controls the current page before initiating download operations. This verification is necessary because the cache warming process relies on fetch requests being intercepted by the service worker; without active control, requests would bypass the Workbox routes and fail to populate the offline caches.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →