IndexedDB
IndexedDB is a client-side database API that lets the browser store and retrieve structured data asynchronously. Unlike Web Storage, which handles only simple strings, IndexedDB supports object-shaped records, indexed lookups, and transactions, making it suitable for offline data, large local caches, and searchable browser-side state.
βΆArchitecture Diagram
π StructureDashed line animations indicate the flow direction of data or requests
Needing browser storage does not automatically mean Web Storage is enough. localStorage is a string key-value store, so structured data is awkward to model, and because the API is synchronous, large reads and writes can freeze the main thread. Once you are dealing with hundreds of messages, offline editing drafts, or searchable local lists, a simple settings store starts to break down. This becomes especially visible in apps that must continue working when the network drops for a while. Server responses cannot live only in memory if the app needs to keep operating. What is needed is a browser-side store that can hold more data, query it efficiently, and do that work asynchronously.
As the web evolved from a document viewer into an application platform, the amount of state that had to live in the browser grew rapidly. Cookies and localStorage covered part of that need at first, but once offline mail, maps, and editors appeared, their limitations became obvious. There were attempts to bring SQL-style storage into the browser through Web SQL, but standardization stalled because it was tied too closely to a particular engine. The standard path became IndexedDB, where the browser exposes its own structured record store with indexes. As offline-first design and the PWA model grew, IndexedDB became the browser's de facto local database.
IndexedDB works by giving the browser its own small database. When the application opens a database, the browser looks up an existing database by name and version. If the version has increased, an onupgradeneeded phase runs, and that is where object stores and indexes are created. An object store is the primary unit that holds records, and an index is an auxiliary structure for quickly finding records by a field value. Actual reads and writes happen inside a transaction. The app opens a readwrite transaction and performs operations such as put, get, and delete, and the browser treats them as one atomic unit. If something fails midway, the operation can be rolled back rather than leaving only part of the change applied. The important difference is that the API is asynchronous. Results do not come back immediately; they are delivered later through events or Promise wrappers. That lets the UI stay responsive even when the browser is handling a large amount of data. For sequential scans, the app uses a cursor, and for targeted lookups, it uses an index.
Opening a database and writing into an object store
const request = indexedDB.open("notes-db", 1);
request.onupgradeneeded = () => {
const db = request.result;
db.createObjectStore("notes", { keyPath: "id" });
};
request.onsuccess = () => {
const db = request.result;
const tx = db.transaction("notes", "readwrite");
tx.objectStore("notes").put({ id: "draft-1", title: "Meeting notes" });
};Follow the `open` -> `upgradeneeded` -> `transaction` sequence to see how IndexedDB is created and written.
Create an index so you do not have to scan everything
const request = indexedDB.open("notes-db", 1);
request.onupgradeneeded = () => {
const store = request.result.createObjectStore("notes", { keyPath: "id" });
store.createIndex("by_updatedAt", "updatedAt");
};
request.onsuccess = () => {
const db = request.result;
const tx = db.transaction("notes");
const recent = tx.objectStore("notes").index("by_updatedAt");
};The index is the part that makes large datasets searchable by order or range instead of full scans.
IndexedDB and Web Storage are both browser stores, but they play different roles. Web Storage is a synchronous string key-value store, so it fits small settings like theme, language, or the last open tab. IndexedDB stores object-shaped data asynchronously and can query it through indexes, which makes it the better fit when the dataset is larger or the lookup conditions are more complex. Another boundary is the difference from a server database. IndexedDB lives separately in each browser, so it is not automatically shared across devices. It does not replace the server as the source of truth. It is closer to a local replica used to reduce round trips and survive temporary offline periods.
IndexedDB is most powerful in offline-first web applications. When a user edits a document or reads mail and the server connection drops for a while, the browser can continue to hold the last synchronized data and the local changes that still need to be uploaded. It is also useful on screens that handle a lot of local data. Product catalogs, message histories, and cached feeds often need field-based lookups across hundreds of items. At that point, keeping everything only in memory becomes fragile, while IndexedDB indexes make local lookup predictable. The main caution is the security boundary. IndexedDB is readable from JavaScript, so if the page suffers an XSS issue, the contents of the local database become exposed as well. It is safer to store data whose disclosure would not immediately turn into account takeover, and to keep the most sensitive secrets out of JavaScript-readable storage.