Engineering 30 min read

IndexedDB vs localStorage: What Your Browser Can Store

MMNMNOTE
indexeddblocalstoragebrowser-storagedexieopfslocal-firstweb-platform
Updated September 23, 2026

Use localStorage when your data fits in five megabytes and is read once on page load. Use IndexedDB when you need to query, paginate, store blobs, or exceed a few kilobytes per user. The two are different categories of storage, not different points on a spectrum — the choice is structural, about API shape, threading model, and capacity, not about taste.

The WHATWG Storage Standard now formally pins localStorage to a quota of "5 × 2²⁰ (i.e., 5 mebibytes)" per origin 1, and the HTML Living Standard defines its API as fully synchronous: every setItem call blocks the main thread until the bytes hit disk 2. IndexedDB, in contrast, is a W3C Working Draft 3.0 (13 August 2025) 3 for an asynchronous, transactional, indexed object store that ships in 96.13% of browsers 4 and can — on Firefox — hold up to 10 GiB of best-effort data per site, or 50% of disk space (capped at 8 TiB) once persistent storage is granted 5. The gap between the two APIs is not a matter of degree. It is a different category of storage.

What localStorage is

localStorage is a synchronous, string-only, key-value store with a 5 MiB-per-origin quota defined by the WHATWG Storage Standard 1. The HTML Living Standard exposes it as a Storage object on window; both keys and values are typed DOMString 2. No transactions, no indexes, no blobs, no async variant. Six methods total. It was designed in 2009 as a slightly-larger cookie that does not round-trip with every HTTP request 6.

The interface is six methods: setItem, getItem, removeItem, clear, length, and key(n) 2. Values are coerced to strings on write and returned as strings on read; storing an object requires JSON.stringify and JSON.parse on every round trip. Writes that exceed the quota throw a QuotaExceededError 2. Reads are O(1) but block the event loop — RxDB's measurements put a small localStorage read at 0.0052 ms and a write at 0.017 ms on Chrome 128 7, faster than IndexedDB per call but synchronous, which is the whole problem at scale. A storage event fires on other documents from the same origin when a value changes 2.

The standardization history clarifies what the API was for. The first Web Storage draft, then called "DOM Storage," was carved out of the larger HTML5 effort as a deliberately small alternative to cookies — designed to hold "megabytes of user data" for performance reasons but not much more 6. It is a cookie that survives reload and skips the request headers. It was never meant to be a database.

What IndexedDB is

IndexedDB is an asynchronous, transactional object store with secondary indexes, cursors, and structured-clone value support, specified by W3C as a Working Draft 3.0 dated 13 August 2025 3. The draft calls an object store "the primary storage mechanism for storing data in a database" 3, and MDN labels IndexedDB "a low-level API for client-side storage of significant amounts of structured data, including files/blobs" 8. Multiple object stores can be opened inside a single transaction, and the database itself is versioned for schema migration 9.

Every operation returns an IDBRequest whose onsuccess or onerror handler fires when the underlying transaction commits or rolls back 10. Three transaction modes — readonly, readwrite, and (for schema migrations) versionchange — guarantee ACID semantics across multiple object stores 9 11. Values are stored via the structured clone algorithm, which means Dates, Maps, Sets, ArrayBuffers, Blobs, and Files round-trip natively — no JSON.stringify 3 8. Secondary indexes are defined at upgrade time and queried through IDBKeyRange cursors. IndexedDB 1.0 became a W3C Recommendation on 8 January 2015; 2.0 on 30 January 2018 added getAll(); 3.0 has been a Working Draft since 11 March 2021 and remains one as of April 2026 12.

Browser support is broad. Caniuse reports 96.13% global support as of April 2026, with full coverage in Chrome 23+, Firefox 10+, Safari 10+, and Edge 79+ 4. The historical gotchas — Safari's flaky early implementations, Firefox's transaction-inactive race with promises 13 — are largely closed; modern wrappers handle the remaining edges.

Side-by-side comparison

The table below is the load-bearing summary. Treat the cells as facts to verify, not conclusions to memorize. Every row cites a primary source — the WHATWG Storage Standard, the W3C IndexedDB draft, MDN, RxDB's Chrome 128 benchmarks, caniuse, the WebKit blog. Skim the table, then read the row you do not believe.

DimensionlocalStorageIndexedDB
Specified byHTML Living Standard (WHATWG, last edit 14 May 2026) 2; Storage Standard, 15 March 2026 1W3C Working Draft 3.0, 13 August 2025 3
Storage limit5 MiB per origin, formally specified 1Firefox: 10 GiB best-effort group / 50% disk persistent (cap 8 TiB) 5. Chrome: up to 60% of disk per origin 14 5. Safari: ~1 GiB then user prompt in 200 MB increments 14 5
Sync / asyncFully synchronous; blocks main thread 15Asynchronous; methods return immediately and resolve via events or promises 3
TransactionsNoneACID transactions in readonly, readwrite, versionchange modes 9 11
Value typesStrings only (DOMString) 2Anything serializable by the structured clone algorithm — objects, arrays, Date, Map, Set, ArrayBuffer, Blob, File, ImageData 3 8
QueriesNone — get by exact key onlyCursors, key ranges, and secondary indexes 3
IndexesNoneMultiple indexes per store; unique and multiEntry flags supported 3
Blob supportNo (would have to base64-encode into a string)Yes — native via structured clone 3 8
Per-call latency (Chrome 128, RxDB)~0.017 ms write / ~0.0052 ms read 7~0.17 ms write / ~0.1 ms read 7
Throughput patternFast per call, but every call blocks; whole tab freezes on large writesSlower per call, but 1,000 documents in one transaction take ~80 ms 16
Browser support (caniuse, Apr 2026)96.27% global 1796.13% global 4
Dev ergonomicsSix methods, no library needed~14 interfaces; raw API is callback-heavy 18 19. Wrappers (Dexie, idb, localForage) close the gap
When to useTiny config flags, feature toggles, last-route memoryNotes, documents, blobs, offline queues, anything you'll query or paginate

Above ~500 KB payloads, IndexedDB cuts page-response time by 40%+ — localStorage's blocking writes monopolize the main thread, and the larger the payload the wider the gap 15. Per-call timings flatter localStorage. Throughput at the tab level does not.

Performance

Performance comparisons are easy to misread. localStorage looks faster per operation; IndexedDB looks faster at scale. RxDB's measurements on Chrome 128 — simple JSON documents — put localStorage writes at 0.017 ms and IndexedDB writes at 0.17 ms 7. That is a 10× per-call gap. It inverts the moment you write enough to block the renderer, because every localStorage call is synchronous and the main thread waits for disk. Batched IndexedDB writes pay one transaction cost across many records and stay off the critical path.

Thomas Steiner, a Chromium engineer, published in August 2024 that Chrome now applies Snappy compression to IndexedDB values stored as external files and moves smaller compressed payloads back into the LevelDB store. The result, per his benchmark: "1 MB structured data payloads are delivered from disk to page in about a quarter the time," with synthetic operations "two to three times faster than before" 20. None of those gains apply to localStorage, because there is nothing in the localStorage spec to compress against 2.

The actual lesson from RxDB's deeper analysis is that IndexedDB's bottleneck is transaction handling, not data throughput. One thousand documents inside a single transaction take about 80 ms; the same thousand spread across one transaction each take roughly two seconds 16. Batch the writes and the gap narrows. Open one transaction per insert and it widens by 25×. localStorage has no such knob — every setItem is its own little transaction, and the main thread waits.

When to use localStorage

Use localStorage for state that is small, string-typed, read once on page load, and rare to write. Theme preference. The last-visited route. A flag for "user dismissed the onboarding banner." Cookies round-trip with every request; localStorage skips that overhead 2. For data that fits in a few kilobytes, the synchronous API is a feature — there is no awaitable promise to thread through your renderer, and reads complete inside the same microtask as the code that asked for them.

The 5 MiB quota is generous for these uses and absurd for almost anything else. A single screenshot, encoded as base64 into a localStorage string, can blow the entire origin budget in one write. The MDN entry for localStorage warns that the protocol partitions storage — http://example.com and https://example.com are separate — and that private-browsing data is cleared when the last private tab closes 21. Plan around both.

When to use IndexedDB

Use IndexedDB when your data is records, not flags — and when you will query by anything other than a primary key. Notes apps, email clients, image editors, offline queues, and PWAs caching API responses all fit. The W3C IndexedDB 3.0 draft (13 August 2025) was built for these shapes: indexed lookups, range cursors, multi-store ACID transactions 3.

Pete LePage, on web.dev, recommends IndexedDB as the default for application data: "For the network resources necessary to load your app, use the Cache Storage API. For file-based content, use the Origin Private File System (OPFS). For other data, use IndexedDB (with a promises wrapper)." 14 That last clause matters. The raw IndexedDB API is event-driven and callback-heavy; almost nobody writes against it directly in 2026. Wrappers (the next section) absorb the ceremony.

Code: the same task in both APIs

The clearest argument for IndexedDB is the diff between storing notes in localStorage and storing them in IndexedDB with an index on updatedAt. localStorage looks shorter only until you query the result — then the missing index forces a full scan and every value is re-parsed from a string. Below: the same task written three ways.

localStorage — store and read a note:

// localStorage: strings only. Everything goes through JSON.
const note = { id: "n_1", title: "Hello", body: "First note", updatedAt: Date.now() };

localStorage.setItem(`note:${note.id}`, JSON.stringify(note));     // blocks main thread

const raw = localStorage.getItem(`note:${note.id}`);                // blocks main thread
const parsed = raw ? JSON.parse(raw) : null;

// Want all notes updated this week? You read every key:
const recent = Object.keys(localStorage)
  .filter((k) => k.startsWith("note:"))
  .map((k) => JSON.parse(localStorage.getItem(k)))
  .filter((n) => n.updatedAt > Date.now() - 7 * 24 * 3600 * 1000);

There is no index on updatedAt. The filter scans every key — and every value is re-parsed from a string on every read.

IndexedDB (raw) — same task with an index on updatedAt:

// Open or upgrade
const open = indexedDB.open("mnmnote", 1);

open.onupgradeneeded = (event) => {
  const db = event.target.result;
  const store = db.createObjectStore("notes", { keyPath: "id" });
  store.createIndex("byUpdatedAt", "updatedAt", { unique: false });   // queryable index
};

open.onsuccess = (event) => {
  const db = event.target.result;
  const tx = db.transaction("notes", "readwrite");                   // explicit transaction
  const store = tx.objectStore("notes");

  store.put({ id: "n_1", title: "Hello", body: "First note", updatedAt: Date.now() });

  tx.oncomplete = () => {
    const ro = db.transaction("notes", "readonly").objectStore("notes");
    const range = IDBKeyRange.lowerBound(Date.now() - 7 * 24 * 3600 * 1000);

    ro.index("byUpdatedAt").openCursor(range).onsuccess = (e) => {
      const cursor = e.target.result;
      if (!cursor) return;
      console.log(cursor.value);                                     // one note per cursor step
      cursor.continue();
    };
  };
};

Every method name is from the W3C draft: createObjectStore, createIndex, transaction, objectStore, put, IDBKeyRange.lowerBound, openCursor, continue 3 10 9 11. The verbosity is real. So is the index.

IndexedDB through Dexie — the same task, half the code:

import Dexie from "dexie";

const db = new Dexie("mnmnote");
db.version(1).stores({ notes: "id, updatedAt" });                    // string DSL = key + indexes

await db.notes.put({ id: "n_1", title: "Hello", body: "First note", updatedAt: Date.now() });

const recent = await db.notes
  .where("updatedAt")
  .above(Date.now() - 7 * 24 * 3600 * 1000)
  .toArray();

Dexie is "a Minimalistic Wrapper for IndexedDB" 18, maintained since 2014 by David Fahlander, currently at v4.4.2 (released 31 March 2026) 22, 14.3k stars on GitHub, "widely used by 100,000 of web sites" per the Dexie homepage 23. Jake Archibald's idb library — 1.19 kB brotli, 7.3k stars — keeps the IDB API surface intact but returns promises 19. Mozilla's localForage (25.8k stars) provides a localStorage-shaped API over IndexedDB for code already written against the synchronous interface 24. Three different libraries, three different opinions about what an IndexedDB wrapper should be. For a full worked tour of Dexie in a notes app — schema, hooks, sync — see our companion post on building offline-first with Dexie.js.

Migration paths

Moving a small app from localStorage to IndexedDB is mechanical: open a versioned database, read every localStorage key once, write the parsed objects into an object store, then localStorage.clear() after a successful commit. Doing it the other direction is rare and usually a mistake — IndexedDB databases routinely exceed the 5 MiB localStorage quota by orders of magnitude.

The tricky part of moving from localStorage is the synchronous-to-asynchronous transition. Code that called JSON.parse(localStorage.getItem("settings")) during render now has to await a promise — which means components either suspend or render with a fallback. React 19's use() hook, which unwraps promises inside Suspense boundaries, helps here 25. So does loading the data once at app boot and caching it in a Zustand or signal store. Code that wrote to localStorage on every keystroke needs throttling; IndexedDB's per-call latency rewards batched writes inside a single transaction 16.

The reverse direction — IndexedDB to localStorage — only makes sense if the data has shrunk to a handful of keys (think: a settings app dropping a feature). Otherwise the 5 MiB ceiling will catch you. The Storage Standard formally pins that figure for localStorage, and the spec does not contemplate raising it 1.

Who should not use IndexedDB

IndexedDB is the wrong tool for four classes of data: tiny configuration flags read once at boot; data that needs to survive seven days of Safari with no user interaction; blobs measured in tens of gigabytes that you'll write byte-by-byte; and small relational datasets you want to inspect live in DevTools without a custom panel. For each of these, IndexedDB is more machinery than the problem deserves.

For the configuration case, a setItem("theme", "dark") call is fewer lines and one synchronous read 2. For the Safari case, the browser deletes all "script-writable storage" — IndexedDB, localStorage, service workers, Cache API — after seven days of no engagement with the origin, per John Wilander's 24 March 2020 WebKit post 26. For the multi-gigabyte byte-by-byte case, OPFS, with its createSyncAccessHandle in a worker, was built for that workload 27 28. For the DevTools-inspection case, Chrome's own docs flag that the IndexedDB DevTools view does not auto-refresh — you have to right-click and reload the database to see new writes — which makes localStorage's instant table view a better fit when you are debugging a five-key settings store 29.

The seven-day Safari rule is the most surprising. PWAs installed to the home screen are exempt (they run on a separate counter tied to app usage) 26, but ordinary websites accessed in Safari tabs get a clock. Apps that need durable browser-only storage on Safari either prompt for persistent storage via navigator.storage.persist() 30 — granted automatically on Chrome based on engagement signals, but only on Firefox after a user prompt 30 — or accept that a quiet user is an evicted user.

What's coming: OPFS, buckets, and Storage 3.0

The Origin Private File System (OPFS) is the most consequential addition since IndexedDB itself. It exposes a per-origin filesystem with in-place writes through FileSystemSyncAccessHandle in Web Workers — fast enough that the SQLite team built @sqlite.org/sqlite-wasm to run a full SQLite database backed by OPFS files 28. MDN calls OPFS files "highly optimized for performance" and notes its "in-place write access to its content," Baseline since March 2023 27.

RxDB's measurements suggest OPFS sync-access handles are roughly 4× faster than IndexedDB for the read-heavy workloads where wa-sqlite shines 16. The trade-off is complexity: OPFS only does synchronous file ops inside a worker, no DevTools view of the files exists, and you have to bring your own query engine. For most note, document, and CRUD apps in 2026, IndexedDB remains the default; OPFS is the upgrade you reach for when query complexity or write throughput demands SQL.

The Storage Standard, edited by Anne van Kesteren and last updated 15 March 2026, also formalizes the storage bucket model — a way for apps to partition their data into independently evictable units, request persistence per bucket, and reason about quota at finer grain than "the whole origin" 1. Buckets are not yet shipped everywhere, but the spec direction is clear: storage on the web is moving from one big bag of bytes per origin to a small filesystem of named, quota-managed compartments.

Frequently Asked Questions

What is the maximum size of localStorage? The WHATWG Storage Standard pins localStorage to a quota of "5 × 2²⁰ (i.e., 5 mebibytes)" per origin 1. In practice browsers honor that figure, with sessionStorage limited separately and the combined Web Storage cap usually 10 MiB. Exceeding it throws a QuotaExceededError and the write is rejected.

Is IndexedDB slower than localStorage? Per operation, yes — RxDB's Chrome 128 measurements put localStorage writes at 0.017 ms versus IndexedDB at 0.17 ms 7. At scale, no. localStorage's synchronous API blocks the main thread, so payloads above ~500 KB make IndexedDB faster overall 15 16.

Can I store images and blobs in IndexedDB? Yes. IndexedDB uses the structured clone algorithm, which natively serializes Blob, File, ArrayBuffer, ImageData, Date, Map, and Set without any string conversion 3 8. localStorage cannot do this — values are strict DOMString, so blobs would have to be base64-encoded (a ~33% size penalty) and the 5 MiB quota would still apply 1 2.

Does IndexedDB work offline? Yes. IndexedDB is a client-side database with no network dependency — once the page loads, reads and writes work entirely against the browser's local store 8. It is the storage backbone of most PWAs, including offline-first note apps and editors, paired with the Cache Storage API for static assets 14.

Will Safari delete my IndexedDB data? Possibly. Per John Wilander's March 2020 WebKit post, Safari evicts all "script-writable storage" — IndexedDB, localStorage, service worker caches — after seven days of Safari use without user interaction on the site 26. PWAs installed to the home screen are exempt. To opt out, request persistent storage via navigator.storage.persist() 30.

Do I need a library to use IndexedDB? Not strictly, but almost everyone uses one. The raw API is event-driven across roughly fourteen interfaces — IDBDatabase, IDBTransaction, IDBObjectStore, IDBRequest, IDBIndex, IDBKeyRange, and friends 10 9 11. Dexie (14.3k stars) wraps it with a fluent query API 22 23; Jake Archibald's idb (7.3k stars, 1.19 kB brotli) keeps the surface intact but returns promises 19; localForage (25.8k stars) presents a localStorage-shaped facade 24.

Is localStorage deprecated? No. The HTML Living Standard's Web Storage section was last edited 14 May 2026, and the Storage Standard's localStorage quota is still formally specified 1 2. localStorage remains the right tool for tiny synchronous configuration. What is deprecated is Web SQL Database, whose specification was halted on 18 November 2010 and explicitly redirects implementers to "Web Storage and Indexed Database API" 31.

Closing

Storage is destiny in browser apps. It is the first decision that compounds — through every render, every migration, every browser quirk. localStorage is the right answer for a string and a flag; IndexedDB is the right answer for a corpus.

For a note app the choice is never close: it stores notes, not preferences, and a note app whose data ceiling is 5 MiB is a note app that ends at roughly 1,500 average-length markdown files. The structural ceiling and the structural floor of the product are the same shape.

If your app is a flag, write a flag. If your app is a notebook, write a database.


References

Grouped by tier. URLs verified live as of 2026-05-15.

Standards / specifications

Engineering / primary technical sources

IndexedDB interface references (MDN)

Engineering blogs and benchmarks

Library sources and ecosystems

Mainstream technical press and community

Academic / canonical

Authoritative engineering voices

Community / additional context

Footnotes

  1. WHATWG. Storage Standard. Editor: Anne van Kesteren (Apple). Last updated 15 March 2026. localStorage quota specified as "5 × 2²⁰ (i.e., 5 mebibytes)" per origin in the registered storage endpoints table. https://storage.spec.whatwg.org/. Accessed 2026-05-15. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9

  2. WHATWG. HTML Living Standard — §Web Storage. Last updated 14 May 2026. Storage interface defined with synchronous setItem, getItem, removeItem, clear, length, key(n). Keys and values typed as DOMString. https://html.spec.whatwg.org/multipage/webstorage.html. Accessed 2026-05-15. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10 ↩11 ↩12

  3. W3C. Indexed Database API 3.0. W3C Working Draft, 13 August 2025. Defines object stores, indexes, key ranges, structured clone for storage, readonly/readwrite/versionchange transaction modes, IDBObjectStore.put/get/openCursor. https://www.w3.org/TR/IndexedDB/. Accessed 2026-05-15. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10 ↩11 ↩12 ↩13

  4. caniuse.com. IndexedDB browser support. 96.13% global support as of April 2026. Full support Chrome 23+, Firefox 10+, Safari 10+, Edge 79+. https://caniuse.com/indexeddb. Accessed 2026-05-15. ↩ ↩2 ↩3

  5. MDN Web Docs. Storage quotas and eviction criteria. Last modified 5 January 2026. Firefox: 10 GiB best-effort group limit; persistent up to 50% of disk capped at 8 TiB. Chrome and Safari quota figures also documented. https://developer.mozilla.org/en-US/docs/Web/API/Storage_API/Storage_quotas_and_eviction_criteria. Accessed 2026-05-15. ↩ ↩2 ↩3 ↩4

  6. WHATWG. HTML Living Standard — §Web Storage introduction. Historical note: "DOM Storage" carved from HTML5, designed to hold "megabytes of user data" beyond cookie capacity. https://html.spec.whatwg.org/multipage/webstorage.html. Accessed 2026-05-15. ↩ ↩2

  7. RxDB. LocalStorage vs. IndexedDB vs. Cookies vs. OPFS vs. WASM-SQLite. Chrome 128.0.6613.137 benchmarks: localStorage write 0.017 ms / read 0.0052 ms; IndexedDB write 0.17 ms / read 0.1 ms. https://rxdb.info/articles/localstorage-indexeddb-cookies-opfs-sqlite-wasm.html. Accessed 2026-05-15. ↩ ↩2 ↩3 ↩4 ↩5

  8. MDN Web Docs. IndexedDB API. "IndexedDB is a low-level API for client-side storage of significant amounts of structured data, including files/blobs." Last modified 3 April 2025. https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API. Accessed 2026-05-15. ↩ ↩2 ↩3 ↩4 ↩5 ↩6

  9. MDN Web Docs. IDBDatabase.transaction(). Last modified 23 June 2025. https://developer.mozilla.org/en-US/docs/Web/API/IDBDatabase/transaction. Accessed 2026-05-15. ↩ ↩2 ↩3 ↩4 ↩5

  10. MDN Web Docs. IDBRequest. https://developer.mozilla.org/en-US/docs/Web/API/IDBRequest. Accessed 2026-05-15. ↩ ↩2 ↩3

  11. MDN Web Docs. IDBTransaction. Defines readonly, readwrite, versionchange modes; oncomplete, onerror, onabort events. https://developer.mozilla.org/en-US/docs/Web/API/IDBTransaction. Accessed 2026-05-15. ↩ ↩2 ↩3 ↩4

  12. Wikipedia. Indexed Database API. IndexedDB 1.0 W3C Recommendation 8 January 2015; 2.0 W3C Recommendation 30 January 2018 (adds getAll()); 3.0 First Public Working Draft 11 March 2021. https://en.wikipedia.org/wiki/Indexed_Database_API. Accessed 2026-05-15. ↩

  13. Archibald, J. (2015). Tasks, microtasks, queues and schedules. Quote on IndexedDB transaction-inactive race with non-Chrome browsers and promise polyfills. https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/. Accessed 2026-05-15. ↩

  14. LePage, P. Storage for the web. web.dev. Last updated 23 September 2024. Pete LePage quote on Cache Storage / OPFS / IndexedDB recommendation; storage-API guidance. https://web.dev/articles/storage-for-the-web. Accessed 2026-05-15. ↩ ↩2 ↩3 ↩4

  15. RxDB. Best Practices for Using LocalStorage in 2026. Last updated 26 March 2026. Quotes the ~10× per-call gap and the "~5–10 MB per domain" practical localStorage limit; warns of main-thread blocking. https://rxdb.info/articles/localstorage.html. Accessed 2026-05-15. ↩ ↩2 ↩3

  16. RxDB. Slow IndexedDB. 1,000 documents in a single transaction = ~80 ms; one transaction per document = ~2 s. https://rxdb.info/slow-indexeddb.html. Accessed 2026-05-15. ↩ ↩2 ↩3 ↩4 ↩5

  17. caniuse.com. Web Storage — name/value pairs. 96.27% global support, April 2026. https://caniuse.com/namevalue-storage. Accessed 2026-05-15. ↩

  18. GitHub. dexie/Dexie.js. "A Minimalistic Wrapper for IndexedDB." 14.3k stars. https://github.com/dexie/Dexie.js. Accessed 2026-05-15. ↩ ↩2

  19. GitHub. jakearchibald/idb. "IndexedDB, but with promises." Author: Jake Archibald. 1.19 kB brotli; 7.3k stars. https://github.com/jakearchibald/idb. Accessed 2026-05-15. ↩ ↩2 ↩3

  20. Steiner, T. (27 August 2024). More efficient IndexedDB storage in Chrome. Chrome for Developers. "1 MB structured data payloads are delivered from disk to page in about a quarter the time." Synthetic ops "two to three times faster than before." https://developer.chrome.com/docs/chromium/indexeddb-storage-improvements. Accessed 2026-05-15. ↩

  21. MDN Web Docs. Window.localStorage. Last modified 30 November 2025. Notes protocol-specific partitioning and private-browsing clearing. https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage. Accessed 2026-05-15. ↩

  22. GitHub. dexie/Dexie.js — releases. Latest: v4.4.2, 31 March 2026. Recent 4.x features include Y.js integration, Blob/string offloading, social auth, IndexedDB 3.0 getAll() optimization. https://github.com/dexie/Dexie.js/releases. Accessed 2026-05-15. ↩ ↩2

  23. Dexie.js homepage. "IndexedDB made simple… widely used by 100,000 of web sites." https://dexie.org/. Accessed 2026-05-15. ↩ ↩2

  24. GitHub. localForage/localForage. "A fast and simple storage library for JavaScript." Originally Mozilla; 25.8k stars; wraps IndexedDB, WebSQL, localStorage. https://github.com/localForage/localForage. Accessed 2026-05-15. ↩ ↩2

  25. React docs. use(). "use is a React API that lets you read the value of a resource like a Promise or context." React 19 reference; integrates with Suspense. https://react.dev/reference/react/use. Accessed 2026-05-15. ↩

  26. Wilander, J. (24 March 2020). Full Third-Party Cookie Blocking and More. WebKit Blog. Seven-day cap on script-writable storage (IndexedDB, localStorage, service worker registrations, Cache API); PWAs installed to home screen exempt. https://webkit.org/blog/10058/full-third-party-cookie-blocking-and-more/. Accessed 2026-05-15. ↩ ↩2 ↩3

  27. MDN Web Docs. Origin private file system. Last modified 14 July 2025. Baseline Widely Available since March 2023. "Highly optimized for performance and offers in-place write access to its content." https://developer.mozilla.org/en-US/docs/Web/API/File_System_API/Origin_private_file_system. Accessed 2026-05-15. ↩ ↩2

  28. GitHub. sqlite/sqlite-wasm. Official "SQLite Wasm conveniently wrapped as an ES Module." OPFS backend available in worker mode. https://github.com/sqlite/sqlite-wasm. Accessed 2026-05-15. ↩ ↩2

  29. Chrome for Developers. View and change IndexedDB data. Author: Kayce Basques. Last updated 18 March 2019. Notes the IndexedDB DevTools panel does not auto-refresh; manual refresh required. https://developer.chrome.com/docs/devtools/storage/indexeddb. Accessed 2026-05-15. ↩

  30. MDN Web Docs. StorageManager.persist(). Baseline Widely Available since December 2021; HTTPS-only; Chrome grants persistence by engagement signals; Firefox prompts the user. https://developer.mozilla.org/en-US/docs/Web/API/StorageManager/persist. Accessed 2026-05-15. ↩ ↩2 ↩3

  31. W3C. Web SQL Database. Web Applications Working Group Note: "Beware. This specification is no longer in active maintenance." Halted 18 November 2010. https://www.w3.org/TR/webdatabase/. Accessed 2026-05-15. ↩