All topics
DOMintermediate

localStorage, sessionStorage, and IndexedDB

The three main client-side storage APIs, ranging from simple synchronous key-value storage to a full asynchronous, transactional database in the browser.

Three storage APIs with very different capabilities.

localStorage is like a small notepad in your pocket — quick to jot a note on, but limited in space and only holds plain text. IndexedDB is like an actual filing room with labeled cabinets, folders, and a card-catalog index system for finding files by any attribute, capable of holding vastly more material, but requiring a bit more setup to use properly.

Key Concepts

1
    localStorage     5-10 MB, synchronous, strings only,
                     persists until cleared
    sessionStorage   the same, cleared when the tab closes
    IndexedDB        hundreds of MB or more, asynchronous,
                     structured, indexed, transactional
2
Web Storage is trivial to use and blocks the main thread.
    localStorage.setItem('user', JSON.stringify(user));
    const user = JSON.parse(localStorage.getItem('user') || 'null');
3
Strings only, so everything is serialised both ways. Being synchronous means a large read blocks rendering — a few hundred kilobytes parsed at startup is measurable.
4
IndexedDB is a real database in the browser.
    const db = await openDB('app', 1, {
      upgrade(db) {
        const store = db.createObjectStore('orders', { keyPath: 'id' });
        store.createIndex('by-date', 'date');
      }
    });
5
    await db.put('orders', order);
    const recent = await db.getAllFromIndex('orders', 'by-date', range);
6
Stores structured objects directly — no serialisation — with indexes, queries and transactions.
7
The raw API is famously awkward, which is why nearly everyone uses a wrapper such as idb. That is worth saying: the API's reputation is deserved and the capability is not the problem.
idb
8
Choosing.
    a preference, a flag, a token   localStorage
    state for this tab only         sessionStorage
    offline data, a large cache,
      anything queried              IndexedDB
    cached HTTP responses           the Cache API
9
Everything is per origin. Different subdomains do not share storage, and nothing is sent to the server automatically — unlike cookies.
10
Nothing expires on its own. An entry written a year ago is still there. Expiry is something you store and check:
11
    { value, expiresAt: Date.now() + 3600000 }
12
And every access can fail. Private browsing, blocked site data and quota limits all make these throw or return nothing.
13
    try { localStorage.setItem(k, v); } catch { /* quota or blocked */ }
14
The page must work when storage is unavailable, which means treating it as a cache rather than as the source of truth.
15
What the interviewer is probing.1. "What is the main performance problem with localStorage?" Probing: synchronicity. Stalls: "It is small." Moves up: it is synchronous, so a large read or write blocks the main thread and the UI.
16
2. "How do they differ in lifetime?" Probing: the scope. Stalls: "They are the same." *Moves up:* localStorage persists until cleared; sessionStorage is per tab and dies with it.
17
3. "When do you need IndexedDB?" Probing: the third option. Stalls: "Never." Moves up: larger structured data, binary blobs, indexes and asynchronous access — storage is a string key-value store only.
18
4. "What happens in private browsing or with blocked cookies?" Probing: the failure mode. Stalls: "It still works." Moves up: access can throw or silently return nothing, so every read and write needs a try/catch and a working fallback.