How TREK Implements Offline Mode and Progressive Web App (PWA) Functionalities
TREK delivers a complete offline travel experience by combining vite-plugin-pwa for PWA installation, Workbox runtime caching for assets and map tiles, Dexie-powered IndexedDB for structured trip data, and a predictive tile prefetcher that caches raster maps before connectivity is lost.
The mauriceboe/TREK repository transforms a React-based travel planner into a standalone Progressive Web App capable of functioning without network connectivity. Through a layered caching architecture that separates static assets from dynamic trip data, TREK ensures travelers can access itineraries, maps, and critical details regardless of internet availability.
PWA Foundation and Service Worker Registration
TREK establishes its PWA capabilities through vite-plugin-pwa, which automates the generation of the Web App Manifest and service worker registration. This configuration enables browsers to install the application as a standalone entity, removing browser chrome and providing native-app-like behavior on both iOS Safari and Android Chrome.
Vite Plugin Configuration
In client/vite.config.js, the PWA plugin registers the service worker with automatic update behavior and defines the application manifest:
import { VitePWA } from 'vite-plugin-pwa'
export default defineConfig({
plugins: [
VitePWA({
registerType: 'autoUpdate',
workbox: {
maximumFileSizeToCacheInBytes: 10 * 1024 * 1024,
globPatterns: ['**/*.{js,css,html,svg,png,woff,woff2,ttf}'],
navigateFallback: 'index.html',
},
manifest: {
display: 'standalone',
// theme colors, icons, start_url defined here
},
}),
],
})
The registerType: 'autoUpdate' setting ensures the service worker updates automatically in the background, while display: 'standalone' in the manifest enables the app to launch without browser UI elements.
Workbox Runtime Caching Strategies
The service worker leverages Workbox to implement sophisticated runtime caching strategies tailored to different resource types. This approach ensures critical assets remain available during offline usage while preventing sensitive user data from persisting in the cache.
Static Assets and CDN Libraries
TREK applies a CacheFirst strategy to external dependencies and core application files:
- CDN libraries (Leaflet, unpkg resources) → Cached for 365 days with a 30-entry limit
- Application shell (JS, CSS, HTML) → Precached during build with navigate fallback to
index.html
Map Tile Caching
Raster map tiles from CartoDB and OpenStreetMap use CacheFirst with aggressive storage limits to support offline navigation:
{
urlPattern: /^https:\/\/[a-d]\.basemaps\.cartocdn\.com\/.*/i,
handler: 'CacheFirst',
options: {
cacheName: 'map-tiles',
expiration: {
maxEntries: 12288,
maxAgeSeconds: 30 * 24 * 60 * 60
}
},
}
The maxEntries: 12288 limit corresponds to approximately 180 MB of raster tiles, matching the hard cap enforced by the tile prefetcher logic.
API Data Handling
TREK distinguishes between cacheable map tiles and dynamic user data:
- Trip metadata API calls →
NetworkFirstwith 5-second timeout, though actual offline reads serve from IndexedDB - User uploads (covers, avatars) →
CacheFirstwith 7-day expiration - All other API calls →
NetworkOnlyto prevent leaking user-specific data through service worker caches
Structured Data Persistence with IndexedDB
While the service worker manages static assets, TREK stores dynamic trip data in IndexedDB using the Dexie.js library. This separation of concerns allows the application to render complete trip interfaces—including places, days, packing items, to-dos, and reservations—without network requests.
Dexie Database Architecture
The client/src/db/offlineDb.ts module defines a structured schema for trip data storage. After successful authentication or trip list refreshes, background sync operations populate the database:
import { offlineDb } from './db/offlineDb'
async function loadTripOffline(tripId: number) {
const trip = await offlineDb.trips.get(tripId)
if (trip) {
renderTrip(trip)
} else {
fetchTripFromServer(tripId)
}
}
This architecture ensures that once a user has viewed a trip online, the entire data bundle persists locally for offline access.
Map Tile Prefetching for Offline Navigation
To guarantee map availability in areas with no connectivity, TREK implements a tile prefetcher that proactively downloads raster tiles within a trip's geographic bounding box.
Bounding Box Calculation and Tile Generation
The client/src/sync/tilePrefetcher.ts module computes a padded bounding box from all place coordinates in a trip, then generates tile URLs for zoom levels 10 through 16:
export async function prefetchTilesForTrip(
tripId: number,
places: Place[],
tileUrlTemplate?: string,
): Promise<void>
The function iterates through the bounding box at multiple zoom levels, constructing URLs using Leaflet's tile URL template pattern.
Cache Limits and Storage Management
The prefetcher implements a hard cap of 12,288 tiles (approximately 180 MB), aligning with the Workbox cache configuration. Once reaching this limit, prefetching stops to prevent storage exhaustion. After completion, the system updates sync metadata in IndexedDB, enabling the UI to display cached tile counts per trip:
const meta = await offlineDb.syncMeta.get(tripId)
if (meta) {
await upsertSyncMeta({
...meta,
tilesBbox: [bbox.minLng, bbox.minLat, bbox.maxLng, bbox.maxLat]
})
}
Fetch requests operate as fire-and-forget operations, allowing the Service Worker's CacheFirst handler to store responses without blocking the main thread.
User Controls and Offline Settings
TREK exposes offline functionality through a dedicated Settings → Offline panel documented in wiki/Offline-Mode-and-PWA.md. This interface displays:
- The number of cached trips and associated tiles
- Pending synchronization actions
- A Re-sync now button for manual updates when online
- A Clear cache button for storage management
These controls provide explicit user agency over local data storage and synchronization timing.
Summary
- PWA Installation: vite-plugin-pwa generates the Web App Manifest and registers an auto-updating service worker, enabling standalone app installation on mobile devices.
- Asset Caching: Workbox runtime strategies cache static files, CDN libraries, and map tiles with specific expiration policies, using
CacheFirstfor offline-critical resources. - Data Persistence: Dexie-powered IndexedDB stores structured trip data (places, reservations, tasks) separately from the service worker cache, ensuring full UI functionality without network access.
- Proactive Caching: The tile prefetcher calculates geographic bounding boxes and downloads up to 12,288 map tiles (approximately 180 MB) before connectivity is lost.
- User Agency: Settings panel provides visibility into cache status and manual controls for synchronization and storage clearing.
Frequently Asked Questions
How does TREK cache map tiles for offline use?
TREK implements a two-layer approach: the Service Worker uses Workbox to cache tiles with a CacheFirst strategy (storing up to 12,288 entries for 30 days), while a dedicated tile prefetcher in client/src/sync/tilePrefetcher.ts proactively downloads tiles within a trip's geographic bounding box before connectivity is lost. The prefetcher calculates bounding boxes from place coordinates, generates URLs for zoom levels 10-16, and respects a hard cap of approximately 180 MB per trip.
What is the difference between the Service Worker cache and IndexedDB in TREK?
The Service Worker cache managed by Workbox stores static assets (JavaScript bundles, CSS, map tiles, and CDN libraries) using HTTP caching strategies like CacheFirst and StaleWhileRevalidate. In contrast, IndexedDB (accessed via Dexie) stores structured application data such as trip metadata, places, packing lists, and reservations. This separation ensures the app shell and maps load from the cache while dynamic user content renders from the database during offline sessions.
How does TREK handle data synchronization when returning online?
When connectivity resumes, TREK relies on the registerType: 'autoUpdate' setting in vite-plugin-pwa to refresh the service worker and application shell automatically. For trip data, the application performs background sync operations that compare local IndexedDB contents with server state, updating the local store with fresh data while queuing any offline modifications for upload. Users can also trigger manual synchronization through the Settings → Offline panel's Re-sync now button.
What storage limits apply to TREK's offline mode?
TREK enforces specific limits to prevent browser storage exhaustion: map tiles are capped at 12,288 entries (approximately 180 MB) per trip in the Workbox cache, matching the prefetcher's hard stop limit. CDN libraries cache up to 30 entries for 365 days, while user uploads (avatars, covers) persist for 7 days. The IndexedDB storage for trip data depends on browser quotas, typically allowing several hundred megabytes of structured data before prompting the user.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →