skip to content

In the browser, how do localStorage and sessionStorage differ in scope and lifetime, and what do the two have in common?

level: juniorimportance: must knowfreq 85%

answer

  1. same interface, two containers
  2. origin is the boundary for both
  3. one outlives the browser restart
  4. the other dies with its tab
  5. reload keeps it, closing the tab does not

basics

~20 s

Both hold string key/value pairs scoped to one origin and share the same API. localStorage persists until something clears it and is visible to every tab on that origin; sessionStorage is confined to a single tab and is discarded when that tab closes.

solid answer

~40 s

They are the same `Storage` interface — `setItem`, `getItem`, `removeItem`, `clear`, `key`, `length` — backed by two different containers, both keyed by origin (scheme + host + port) and both string-only and synchronous. `localStorage` has no expiry: it survives reloads, tab closes and browser restarts until your code, the user, or the browser's eviction machinery removes it, and every tab and window on that origin reads and writes the same data. `sessionStorage` is scoped to one tab's top-level browsing context: it survives reloads and same-tab navigations, a same-origin iframe in that tab shares it, but a second tab on the same site gets its own empty store and the data is gone when the tab closes. Neither is exposed inside a Web Worker, and a private window keeps its own separate store.

code

javascript · 8 lines
javascript
sessionStorage.setItem('wizardStep', '2');
localStorage.setItem('theme', 'dark');

// visible only in the tab that wrote it
console.log(sessionStorage.getItem('wizardStep')); // "2"

// visible in every tab and window on this origin
console.log(localStorage.getItem('theme')); // "dark"

go deeper

for a junior

Be ready to state the two differences plainly: localStorage is per-origin and persists until cleared, sessionStorage is per-tab and dies with the tab. Say that both store strings only.

for a middle

Explain what origin means here (scheme, host and port) and why a subdomain gets a separate store, and describe how sessionStorage behaves on reload, on a manually opened tab, and on a tab opened from the page.

for a senior

Show judgment about which data belongs in which store in a real app — per-tab context versus per-user preference — and mention the shared constraints that decide when neither is appropriate: synchronous access, string-only values, no worker access.

for a principal

Own the policy: what the app is allowed to keep on the client at all, how the storage choice interacts with multi-account and multi-tab sessions, and what the migration path looks like when the shape of persisted state changes across releases.

## One API, two containers Web Storage is defined by the HTML standard as a single interface, `Storage`, exposed twice on `window`: as `localStorage` and as `sessionStorage`. The methods are identical — `setItem(key, value)`, `getItem(key)`, `removeItem(key)`, `clear()`, `key(index)` and the `length` property — and both areas store only strings, synchronously, on the main thread. Every difference between them is about *which container* your call lands in, and *how long that container lives*. ## The origin is the boundary Both areas are partitioned by origin, meaning the triple of scheme, host and port. `https://example.com` and `http://example.com` are different origins, so they have different stores. `https://app.example.com` and `https://example.com` are different origins too — there is no equivalent of a cookie's domain attribute that lets a parent domain and a subdomain share one Web Storage area. Ports matter as well, which is why a dev server on `:3000` and one on `:5173` never see each other's data. ```js // on https://example.com localStorage.setItem('theme', 'dark'); // on https://app.example.com — a different origin, so: localStorage.getItem('theme'); // null ``` ## localStorage: origin-wide and effectively permanent `localStorage` has no expiry mechanism at all. There is no TTL parameter, no max-age, nothing analogous to a cookie's expiry. A value written once is still there after a reload, after the tab is closed, and after the browser is restarted. It disappears only when your code removes it, when the user clears site data, or when the browser evicts the origin's data under storage pressure. If you want expiring data you have to build it yourself — store a timestamp alongside the value and check it on read. Its other defining property is that it is shared by every tab, window and same-origin iframe of that origin in the same browser profile. Two tabs open on your app are looking at one store, which is exactly why the `storage` event exists to tell the other tabs that something changed. ## sessionStorage: one tab, one store `sessionStorage` is scoped to a tab's top-level browsing context. Inside that tab it is quite durable: it survives a reload, a hard reload, and navigations to other pages of the same origin. It does *not* travel to a second tab — open your site in a new tab manually and its `sessionStorage` starts empty. A same-origin iframe inside the tab shares the tab's session store; a cross-origin iframe gets its own, keyed by its own origin. One detail people get wrong: browsers clone the session store into an auxiliary tab opened *from the page*, for example by `window.open()` or a link with `target="_blank"`. It is a copy, not a live link — writes afterwards do not propagate between the two tabs. When the tab is closed the store is dropped, although browsers that offer "reopen closed tab" or session restore usually bring it back with the tab. ## Choosing between them Ask whether the data describes *this tab* or *this user on this site*. Wizard progress, a scroll position for the current view, a "you are acting as account X in this tab" selection, a one-time redirect target captured before login — all of those are per-tab and belong in `sessionStorage`, where a second tab correctly starts fresh. Theme preference, a dismissed-banner flag, a cached list you would rather not refetch, a feature-flag snapshot — those describe the user on the origin and belong in `localStorage`. ## What they share, and what that costs Because both are the same interface, both inherit the same constraints. Values and keys are strings, so anything structured has to be serialised by you. Access is synchronous and blocks the main thread. Neither object exists in a `Worker` scope, so a worker that needs persistence uses IndexedDB or the Cache API instead. Both live under a modest per-origin limit — commonly around 5 MB, exact figure browser-dependent — and both throw when that limit is hit. And in a private/incognito window the browser gives the origin a separate ephemeral store that is discarded when the private session ends, so nothing written there survives. ## The usual interview traps "sessionStorage clears on reload" is wrong — only closing the tab clears it. "localStorage is shared across subdomains" is wrong — that is cookie behaviour. "localStorage expires after a while" is wrong — it has no expiry, only eviction. And "I'll read it from my worker" does not compile, because the property is not there.

  • A user opens your app in a second tab. Which of the two stores does that new tab see the existing data in?
    It sees everything already in `localStorage`, because that store belongs to the origin, not the tab. Its `sessionStorage` starts empty — unless the tab was opened *from* the page via `window.open()` or `target="_blank"`, in which case the browser clones the opener's session store into it as a one-time copy that does not stay in sync.
  • How would you give a localStorage entry an expiry, since the API has none?
    Store the payload together with an expiry timestamp — for example a serialised `{ value, expiresAt }` — and check `Date.now() > expiresAt` on read, removing the key and returning nothing when it has passed. There is no browser-side TTL, so the check has to happen in your read path; a purge on startup keeps stale keys from accumulating against the quota.
  • Can a Web Worker read localStorage to share state with the page?
    No. Web Storage is exposed on `Window` only, so `localStorage` and `sessionStorage` simply do not exist in a worker scope. A worker that needs persistence uses IndexedDB or the Cache API, both of which are available there; to hand data to a worker directly you post it with `postMessage`.

localStorage is the filing cabinet in the office that everyone at that address shares; sessionStorage is the sticky note on one desk that gets thrown away when that desk is cleared.

saying these in an interview costs you the question

  • Says sessionStorage is cleared by a page reload
  • Thinks localStorage is shared across subdomains like a cookie
  • Claims two tabs on the same site share one sessionStorage
  • Says localStorage entries expire automatically after some period
  • Believes localStorage is available inside a Web Worker

context