BizTechLab

IDEASINNOVATIONIMPACT

Web APIs

localStorage

Persistent, synchronous key-value storage built into every browser.

1 August 20269 min read

Overview

localStorage is a browser API that lets a website store key-value string pairs in the user's browser, with no expiration date. Unlike a cookie, nothing stored in it is ever sent to the server automatically — it exists purely on the client, scoped to the page's origin (protocol + domain + port).

Why It Exists

Before localStorage (part of the Web Storage API, standardized in HTML5), the only client-side persistence mechanism was cookies — and cookies were never designed for this job. Every cookie is sent with every HTTP request to the same domain, so using them for anything beyond a few small tokens meant paying a bandwidth and latency cost on every request, for data the server usually didn't even need. localStorage solves this by keeping data entirely on the client, with a much larger capacity, and zero network overhead.

Real World Example

A theme toggle (light/dark mode) is the canonical example: the user's preference is saved to localStorage on click, and read back on every page load — before React or any framework even hydrates — so the correct theme applies instantly with no flash of the wrong theme. Shopping cart drafts, unsaved form data, and "don't show this again" dismissible banners are other common uses.

How It Works

Every origin gets its own isolated localStorage instance, exposed on `window.localStorage`. It stores everything as strings — objects and arrays must be serialized with `JSON.stringify()` before storing and parsed back with `JSON.parse()` after reading. All operations are synchronous, which is simple to use but means large reads/writes can briefly block the main thread.

Diagram

How data flows between a page and localStorage — entirely client-side

Page calls setItem('key', 'value')

Browser writes to disk

the origin's local storage partition

Data persists

across tabs, page reloads, and browser restarts

Page calls getItem('key')

reads it back — no network request involved

Syntax

// Write
localStorage.setItem("theme", "dark");

// Read
const theme = localStorage.getItem("theme"); // "dark" | null

// Remove one key
localStorage.removeItem("theme");

// Remove everything for this origin
localStorage.clear();

Methods

setItem(key, value)

Stores a key-value pair. Both are coerced to strings.

getItem(key)

Returns the string value, or null if the key doesn't exist.

removeItem(key)

Deletes a single key-value pair.

clear()

Deletes every key-value pair for the current origin.

key(index)

Returns the name of the key at a given numeric index — rarely used directly.

Common Mistakes

Calling localStorage during server-side rendering

Why: `window` doesn't exist on the server. In a Next.js Server Component, or during the first SSR pass of a Client Component, accessing `localStorage` throws a ReferenceError.

Fix: Only read/write localStorage inside a `useEffect` (which runs client-side only) or behind a `typeof window !== "undefined"` guard.

Storing sensitive data (tokens, PII) in localStorage

Why: Any JavaScript running on the page — including injected via an XSS vulnerability — has full read/write access to localStorage. There's no HttpOnly-style protection like cookies have.

Fix: Keep auth tokens in memory or an HttpOnly cookie instead; use localStorage only for non-sensitive UI state.

Not handling the storage quota limit

Why: Most browsers cap localStorage around 5–10MB per origin. Exceeding it throws a QuotaExceededError that, if unhandled, can silently break the feature.

Fix: Wrap writes in a try/catch, and treat localStorage as best-effort, not guaranteed storage.

Forgetting JSON.stringify / JSON.parse

Why: localStorage only stores strings. Storing an object directly silently stores the string "[object Object]" instead of the actual data.

Fix: Always serialize on write and parse on read, and guard the parse in a try/catch in case the stored value is malformed.

Interview Questions

beginner

What's the difference between localStorage and sessionStorage?

Both share the same API. localStorage persists indefinitely (until explicitly cleared); sessionStorage is cleared when the tab is closed and isn't shared between tabs, even for the same origin.

intermediate

Why doesn't localStorage work during server-side rendering, and how do you fix it in a framework like Next.js?

`localStorage` is a `window` property, and `window` doesn't exist in a Node.js server environment. Access it only inside a `useEffect` hook or a `typeof window !== "undefined"` guard so it only runs after the component mounts in the browser.

senior

Why is localStorage considered unsafe for storing authentication tokens, and what's the alternative?

localStorage has no origin-level protection against script access — any XSS vulnerability on the page can read every key and exfiltrate it. HttpOnly cookies are inaccessible to JavaScript entirely, which removes that specific attack vector (though they introduce CSRF considerations that need their own mitigation, like SameSite cookies and CSRF tokens).

Production Best Practices

Do

Wrap every localStorage call in a try/catch — quota errors and disabled storage (private browsing in some browsers) both throw.

Namespace your keys (e.g. `biztechlab:theme`) to avoid collisions with other scripts on the same origin.

Treat it as a cache, not a database — always have a fallback if the value is missing or corrupted.

Don't

Don't store auth tokens, passwords, or personal data in it.

Don't assume it's available — private browsing modes and some browser settings disable it entirely.

Don't store large payloads — it's synchronous, so big reads/writes can jank the UI.

Comparison

localStoragesessionStorageCookiesIndexedDB
PersistenceUntil clearedUntil tab closesConfigurable expiryUntil cleared
Capacity~5–10MB~5–10MB~4KBHundreds of MB+
Sent with HTTP requestsNoNoYes, automaticallyNo
API styleSynchronousSynchronousString parsingAsynchronous
Best forSmall UI statePer-tab stateServer-readable tokensStructured/large data