← Back to docsRecipe

IndexedDB Primer

A transactional object store baked into every modern browser — perfect for offline-first apps, large binary blobs, and structured data that outgrows localStorage.

Why IndexedDB

localStorage is synchronous, string-only, and capped at ~5 MB. IndexedDB is asynchronous, stores structured clones (objects, File, Blob, ArrayBuffer), and has no practical size limit — the browser prompts the user before crossing ~50 MB. It is the canonical storage layer for Progressive Web Apps and any tool that needs to survive a tab crash.

Mental model

IndexedDB is not a relational database. Think of it as a persistent Map where each key opens an object store. Every read or write happens inside a transaction. Transactions auto-commit when their microtask queue drains, so batch your puts.

The three-step dance

  1. indexedDB.open(name, version) — fires onupgradeneeded exactly once per version bump. Create stores and indexes here.
  2. Open a readwrite transaction on the store, get a reference, and call put, get, or getAll.
  3. Listen to transaction.oncomplete — the request-level success event fires before the transaction commits, so the durable signal is the transaction event.

Indexes and cursors

Create an index on a property to query without loading every record. Use IDBKeyRange.bound for range scans. Cursors let you walk results one-by-one, including deleting or updating in-place — essential for large datasets where getAll would allocate too much memory.

The promise wrapper you'll write anyway

The native API is event-driven. Wrap it in a thin promise layer that resolves on transaction complete and rejects on transaction error. Never mix raw callbacks and promises in the same transaction — the auto-commit behavior will surprise you.

Meridian tip: Use IndexedDB as the offline cache backing the license heartbeat. Store the last-known-good HMAC, machine fingerprint, and grace window expiry. On startup, read from IndexedDB before hitting the network — instant launch, even when the auth server is unreachable.