localStorage: 5MB of strings, synchronous (blocks the page). IndexedDB: gigabytes of structured data, files included, async. Offline apps live here.
The raw API (verbose but honest)
const req = indexedDB.open("notesApp", 1);
req.onupgradeneeded = () => {
req.result.createObjectStore("notes", { keyPath: "id", autoIncrement: true });
};
req.onsuccess = () => {
const db = req.result;
const tx = db.transaction("notes", "readwrite");
tx.objectStore("notes").add({ title: "CN Unit 3", body: "...", date: Date.now() });
};The wrapper everyone actually uses
// with the tiny `idb` library โ Promise-based sanity
import { openDB } from "idb";
const db = await openDB("notesApp", 1, {
upgrade(db) { db.createObjectStore("notes", { keyPath: "id", autoIncrement: true }); }
});
await db.add("notes", { title: "CN Unit 3" });
const all = await db.getAll("notes");Decision table
- Settings, theme, small flags โ localStorage
- Notes, drafts, cached API data, images/blobs, anything queryable or >1MB โ IndexedDB
- Offline PWA data layer โ IndexedDB + service worker (they're a pair)
Interview line: "localStorage is a synchronous string bucket; IndexedDB is an async transactional object database."