Offline Storage
PWA storage has two jobs: cache resources so the app opens, and store user data so work is not lost.
Storage Choices
| Storage | Use for | Avoid for |
|---|---|---|
| Cache Storage | HTML, JS, CSS, images, fonts, offline fallback pages, safe API responses | Structured records, drafts, business data |
| IndexedDB | Records, blobs, upload queues, sync metadata, migrations | Simple one-off preferences if Web Storage is enough |
| Web Storage | Tiny preferences | Large data, async flows, sensitive records, offline databases |
| Storage Manager | Quota, usage, persistence requests | Business logic storage itself |
IndexedDB Shape
const request = indexedDB.open('ledger', 1);
request.onupgradeneeded = () => {
const db = request.result;
db.createObjectStore('entries', { keyPath: 'id' });
db.createObjectStore('outbox', { keyPath: 'id' });
};
request.onsuccess = () => {
const db = request.result;
const tx = db.transaction('entries', 'readwrite');
tx.objectStore('entries').put({
id: crypto.randomUUID(),
amount: 1200,
synced: false,
});
};
Eviction Reality
Browsers manage storage under origin quota. Users can clear data. Private browsing can behave differently. Persistent storage can reduce eviction risk, but it is not a backup.
if ('storage' in navigator && 'persist' in navigator.storage) {
const persisted = await navigator.storage.persist();
console.log(`Persistent storage granted: ${persisted}`);
}
Gotcha: Offline-first apps need export or backup. IndexedDB persistence is not a disaster recovery plan.