Offline Storage For PWAs

A complete guide to implementing offline storage in Progressive Web Apps using service workers, IndexedDB, and the Cache API for reliable cross-platform mobile experiences.

Why Offline Capability Matters for Mobile Apps

The modern mobile user journey rarely follows a linear, always-online path. Consider the commuter who starts reading an article on the subway with spotty coverage, the sales professional who needs to access customer data during a flight, or the field worker who operates in areas with no cellular service at all. In each scenario, the difference between a useful app and a frustrating one is the ability to function without a network connection.

Offline storage in PWAs serves multiple critical purposes:

  • Continuity: Users can begin tasks online and continue them offline without losing progress
  • Performance: Cached content serves instantly rather than waiting for network requests
  • Data Conservation: Previously fetched resources don't need re-downloading
  • Resilience: Applications work reliably despite inevitable network failures

For developers working across iOS and Android platforms, PWAs offer a unique advantage: a single codebase that provides offline capabilities across all platforms where browsers run. Our mobile app development services help businesses leverage these capabilities to reach users regardless of their connectivity.

The Three Pillars of PWA Storage

Understanding when to use each storage mechanism is essential for building robust offline-capable applications.

Cache API

Stores network requests and responses for static assets like HTML, CSS, JavaScript, images, and fonts. Works with service workers to serve cached content when offline.

IndexedDB

A transactional database system for storing significant amounts of structured data, including JavaScript objects and binary files. Asynchronous and queryable.

Web Storage

Simple key-value storage via localStorage and sessionStorage for user preferences, session state, and small configuration objects.

Service Workers: The Foundation of Offline Functionality

Service workers are the backbone of PWA offline functionality, acting as a programmable network proxy that sits between your web application and the network. Unlike regular JavaScript that runs on the main thread, service workers run in a separate thread and have a lifecycle entirely independent of the web page.

The service worker lifecycle consists of three main phases:

  1. Installation: The service worker script is downloaded and executed, allowing you to set up caches and prepare for offline operation.
  2. Activation: The service worker takes control of pages within its scope, replacing any previous service workers and cleaning up old caches.
  3. Fetch: The service worker intercepts every network request made by pages within its scope, giving you the opportunity to serve cached content.

For mobile developers, service workers provide the same capabilities across iOS and Android browsers, creating a consistent offline experience. Apple's Safari on iOS has supported service workers since iOS 16.4, meaning your offline implementation will work on the vast majority of mobile devices. Implementing service workers is a key component of our web development services that ensure robust, performant applications.

Service Worker Cache-First Strategy
1const CACHE_NAME = 'app-v1';2const STATIC_ASSETS = [3 '/',4 '/index.html',5 '/styles/main.css',6 '/scripts/app.js',7 '/images/logo.png',8 '/offline.html'9];10 11// Install event - cache essential assets12self.addEventListener('install', (event) => {13 event.waitUntil(14 caches.open(CACHE_NAME).then((cache) => {15 return cache.addAll(STATIC_ASSETS);16 })17 );18});19 20// Fetch event - serve from cache, fall back to network21self.addEventListener('fetch', (event) => {22 event.respondWith(23 caches.match(event.request).then((cachedResponse) => {24 return cachedResponse || fetch(event.request).then((networkResponse) => {25 const responseToCache = networkResponse.clone();26 caches.open(CACHE_NAME).then((cache) => {27 cache.put(event.request, responseToCache);28 });29 return networkResponse;30 });31 }).catch(() => {32 if (event.request.mode === 'navigate') {33 return caches.match('/offline.html');34 }35 })36 );37});

Web Storage: Simple Key-Value Storage

Web Storage provides the simplest mechanism for storing small amounts of data on the client side. The API offers two storage mechanisms:

  • localStorage: Persists data until explicitly deleted, ideal for user preferences and application settings
  • sessionStorage: Persits data only for the duration of the page session, perfect for temporary state during multi-step processes

While Web Storage offers simplicity, it comes with limitations: synchronous operations can block the main thread, only strings can be stored (requiring serialization), and localStorage is typically limited to about 5MB per origin. More critically, Web Storage is not available within service worker contexts.

Storing User Preferences with localStorage
1const userPreferences = {2 theme: 'dark',3 fontSize: 'medium',4 notificationsEnabled: true,5 language: 'en-US'6};7 8// Serialize and store9localStorage.setItem('userPreferences', JSON.stringify(userPreferences));10 11// Retrieve and parse12const storedPreferences = JSON.parse(localStorage.getItem('userPreferences'));13 14// Check if storage is available (important for private browsing)15function isStorageAvailable() {16 try {17 const test = '__storage_test__';18 localStorage.setItem(test, test);19 localStorage.removeItem(test);20 return true;21 } catch (e) {22 return false;23 }24}

IndexedDB: Structured Data Storage for Complex Applications

IndexedDB represents the gold standard for storing application data in PWAs. Unlike Web Storage's synchronous, string-only approach, IndexedDB provides an asynchronous, transactional database system capable of storing significant amounts of structured data.

For developers building cross-platform mobile applications, IndexedDB's browser-native implementation means you get database functionality without external libraries. Your IndexedDB code works identically on iOS Safari, Android Chrome, desktop Chrome, Firefox, and Edge.

The combination of IndexedDB and the Cache API gives PWAs nearly the same offline capabilities as native apps, but using web technologies that work across platforms. This approach is essential for businesses building AI automation solutions that need to function reliably regardless of network connectivity.

Opening and Configuring IndexedDB
1const dbName = 'TaskManagerDB';2const dbVersion = 1;3 4function openDatabase() {5 return new Promise((resolve, reject) => {6 const request = indexedDB.open(dbName, dbVersion);7 8 request.onerror = (event) => {9 console.error('Database error:', event.target.error);10 reject(event.target.error);11 };12 13 request.onsuccess = (event) => {14 const db = event.target.result;15 resolve(db);16 };17 18 request.onupgradeneeded = (event) => {19 const db = event.target.result;20 21 // Create tasks object store with auto-incrementing key22 if (!db.objectStoreNames.contains('tasks')) {23 const taskStore = db.createObjectStore('tasks', {24 keyPath: 'id',25 autoIncrement: true26 });27 28 // Create indexes for querying29 taskStore.createIndex('status', 'status', { unique: false });30 taskStore.createIndex('dueDate', 'dueDate', { unique: false });31 taskStore.createIndex('createdAt', 'createdAt', { unique: false });32 }33 };34 });35}

Cache API: Storing Network Resources

The Cache API provides a way to cache network requests and responses, making it ideal for storing static assets, API responses, and resources that need to be available when offline. Unlike IndexedDB, which stores application data, the Cache API is specifically designed for HTTP-level caching.

For cross-platform mobile developers, the Cache API is essential for achieving fast load times and reliable offline functionality. By caching HTML pages, CSS stylesheets, JavaScript bundles, images, and fonts during initial load, your PWA can serve these resources instantly from local storage.

Different content types require different caching strategies:

  • Cache-first: Prioritizes speed and offline availability for static assets
  • Network-first: Prioritizes freshness for content that needs to be reasonably current

Storage Quotas and Browser Limitations

Understanding browser storage limits is crucial for building PWAs that work reliably across devices and browser configurations.

Storage TypeTypical LimitBest For
localStorage/sessionStorage~5MBPreferences, small config
IndexedDB, Cache APIUp to 60% of disk spaceLarge data, static assets
Origin Private File SystemUp to 60% of disk spaceFile operations

For a device with a 64GB disk, IndexedDB and Cache API could provide up to approximately 38GB of storage. However, this quota is shared across all storage mechanisms and all origins.

Browsers may evict cached data when device storage becomes low. By default, cached data is considered non-persistent and can be automatically cleared. For critical data, request persistent storage using the Storage Manager API.

Checking Storage Quota with Storage Manager API
1// Check available storage quota2async function getStorageEstimate() {3 if ('storage' in navigator && 'estimate' in navigator.storage) {4 const estimate = await navigator.storage.estimate();5 6 console.log(`Storage quota: ${(estimate.quota / 1024 / 1024 / 1024).toFixed(2)} GB`);7 console.log(`Used: ${(estimate.usage / 1024 / 1024 / 1024).toFixed(2)} GB`);8 console.log(`Available: ${((estimate.quota - estimate.usage) / 1024 / 1024 / 1024).toFixed(2)} GB`);9 10 return estimate;11 }12 13 console.warn('Storage Manager API not supported');14 return null;15}16 17// Request persistent storage18async function requestPersistentStorage() {19 if ('storage' in navigator && 'persist' in navigator.storage) {20 const persistent = await navigator.storage.persist();21 22 if (persistent) {23 console.log('Storage will not be automatically cleared');24 } else {25 console.log('Storage may be cleared under storage pressure');26 }27 28 return persistent;29 }30 31 console.warn('Persistent storage not supported');32 return false;33}

Best Practices for PWA Offline Storage

Offline-First Architecture

Build your PWA to work fully offline and gracefully enhance functionality when connected. Store data locally before syncing to the server rather than fetching from the server and optionally caching.

Data Synchronization

Implement a sync queue pattern that stores pending operations in IndexedDB and processes them when connectivity returns. Each queued operation should include operation type, affected data, and a unique identifier.

Cache Versioning

Use cache names that include version numbers to maintain multiple cache versions and clean up old versions during activation. When deploying a new version, increment the cache version number.

Security Considerations

Never store sensitive data in localStorage as it's accessible to any JavaScript and vulnerable to XSS attacks. Use IndexedDB for sensitive data and consider encrypting values before storage using the Web Crypto API. Our web development services include security-first approaches to data handling.

Frequently Asked Questions

Build Offline-Capable Mobile Apps

Our team specializes in building Progressive Web Apps with robust offline functionality for iOS, Android, and web platforms.

Sources

  1. MDN Web Docs - Offline and Background Operation - Comprehensive guide on service workers, caching strategies, and offline functionality
  2. Microsoft Learn - Store Data on the Device - Detailed comparison of storage options with quotas and data eviction policies
  3. Monterail - Make Your PWA Work Offline Part 2: Dynamic Data - Best practices for storing and synchronizing dynamic data
  4. LogRocket - Offline-first Frontend Apps in 2025 - Modern approaches to offline storage
  5. MDN Web Docs - Service Worker API - Official service worker documentation
  6. MDN Web Docs - IndexedDB API - Official IndexedDB documentation
  7. MDN Web Docs - Cache API - Cache API reference for PWA resource caching