Side by side
IndexedDBvsLocal Storage
What is the difference between IndexedDB and localStorage?
Updated 3 min read8 differences
In short
localStorage is a small, synchronous store of strings; IndexedDB is an asynchronous browser database for large structured data and files.
IndexedDB
Indexed Database API
IndexedDB is a database built into web browsers that stores large amounts of structured data and files on the user's device, with indexes and transactions.
Read the page on IndexedDBLocal Storage
Local storage is a browser feature that lets a website save text as key-value pairs on the user's device, where it stays even after the browser is closed.
Read the page on Local StorageIndexedDB and Local Storage compared
| Aspect | IndexedDB | Local Storage |
|---|---|---|
| Type | A full database with object stores, indexes and transactions | A simple key-value store |
| Data stored | Objects, arrays, dates and files, without conversion | Strings only; objects go through JSON.stringify |
| Capacity | Often hundreds of megabytes or more, depending on free disk | About 5 MB per origin |
| API style | Asynchronous: requests with events, or promises through a wrapper | Synchronous: each call blocks the page until it finishes |
| Lookups | By key, by index ranges and with cursors | By exact key only |
| Transactions | Yes; a group of writes succeeds or fails as a whole | None; each setItem stands alone |
| In workers | Available in web workers and service workers | Only in the page itself, not in workers |
| Best for | Offline data, cached API responses, large lists, files | Small settings: theme, language, flags, a short draft |
The difference, explained
Both keep data in the browser, on the user's device, separately for each origin. localStorage is a simple key-value store: strings under string keys, written with setItem and read with getItem. IndexedDB is a full database built into the browser: object stores hold JavaScript objects under keys, indexes find records by other fields, and transactions group writes that either all succeed or all fail.
The essential differences are size, data model and timing. localStorage is limited to about 5 MB per origin, stores only strings, so objects pass through JSON.stringify, and its calls are synchronous, blocking the page while they run. IndexedDB can usually use a large share of the free disk, stores objects, dates and files directly, and is asynchronous, so big reads and writes don't freeze the interface. It also works in web workers and service workers, where localStorage is not available.
Many apps that use IndexedDB use localStorage as well. A theme, a language or a feature flag is a few bytes that the page wants at once, so localStorage is simpler; an offline mailbox, cached API responses or a list of thousands of drafts belongs in IndexedDB. The raw IndexedDB API is verbose and event-based, so many projects use a small wrapper such as idb, which adds promises, or Dexie.js.
A common misconception is that IndexedDB is a SQL database in the browser. It has no tables or queries, only keys, indexes and cursors that step through records. Another is that either store is permanent or private: browsers may clear both when space runs low unless navigator.storage.persist() is granted, and any script on the page, including one injected through XSS, can read both.
Which one should you use?
Choose IndexedDB when…
- You store a lot of data, such as an offline copy of records or files.
- You need to look records up by fields other than the key.
- Several writes must succeed or fail together.
- A web worker or service worker needs the data.
Choose Local Storage when…
- You store a few small values, such as a theme or a dismissed banner.
- The page must read the value at once, for example to apply a theme before the first paint.
- Simplicity matters more than capacity or structure.
Saving data each way (JavaScript)
// IndexedDB, here through the small idb wrapper: objects, indexes, async
import { openDB } from "idb";
const db = await openDB("mail", 1, {
upgrade(db) {
const store = db.createObjectStore("messages", { keyPath: "id" });
store.createIndex("byDate", "receivedAt");
},
});
await db.put("messages", { id: 7, subject: "Hi", receivedAt: new Date() });
const byDate = await db.getAllFromIndex("messages", "byDate");// localStorage: strings only, synchronous, no indexes
const prefs = { theme: "dark", fontSize: 16 };
localStorage.setItem("prefs", JSON.stringify(prefs));
const saved = JSON.parse(localStorage.getItem("prefs") ?? "{}");
saved.theme; // "dark", available at once, no awaitReaders ask
How much more can IndexedDB store than localStorage?
Much more. localStorage stops at about 5 MB per origin, while IndexedDB shares the origin's storage quota, which in modern browsers is often hundreds of megabytes or more; navigator.storage.estimate() reports the actual figure.
Is localStorage faster than IndexedDB?
For one small value, reading localStorage is quick and needs no await. For many or large records it is worse, because every call blocks the page, while IndexedDB works asynchronously and can fetch just the records an index points to.
Can I use IndexedDB in a service worker?
Yes, and it is the usual place for offline data there. localStorage is not available in service workers or in other web workers.