Quick Answer

localStorage persists until cleared and holds about 5 MB. sessionStorage clears when the tab closes. Cookies are small and get sent to the server on every request. Store preferences in localStorage; do not store authentication tokens there.

The API is four methods

localStorage.setItem("theme", "dark");
console.log(localStorage.getItem("theme"));  // "dark"
localStorage.removeItem("theme");
localStorage.clear();

sessionStorage has exactly the same methods. The only difference is lifetime: localStorage survives closing the browser, sessionStorage is wiped when the tab closes and is not shared between tabs.

Reading a key that does not exist returns null, not undefined — worth knowing when writing the check.

Everything is a string, which catches everyone

localStorage.setItem("count", 5);
const n = localStorage.getItem("count");
console.log(typeof n);      // "string"
console.log(n + 1);         // "51"  <-- concatenation, not addition

Storage holds strings only. The number 5 became "5", and adding 1 concatenated. Convert on the way out with Number(n) or parseInt(n, 10).

Objects and arrays need JSON on both sides:

const user = { name: "Asha", marks: [91, 88] };
localStorage.setItem("user", JSON.stringify(user));

const back = JSON.parse(localStorage.getItem("user"));
console.log(back.marks[0]);  // 91

Forgetting stringify stores the literal text "[object Object]", which is a distinctive and frequently-seen bug. And wrap JSON.parse in a try/catch — stored data can be corrupted or left over from an older version of your app, and an exception at startup breaks the whole page.

How cookies differ

Cookies predate both and behave differently in ways that matter:

  • They are sent to the server automatically with every request to that domain. localStorage is never sent anywhere unless your code sends it.
  • They are small — roughly 4 KB each, against about 5 MB for localStorage.
  • They can expire at a set time.
  • They can be made invisible to JavaScript using the HttpOnly flag.

That automatic transmission is the whole point: it is how a server recognises you across requests, which is why sessions use cookies. It is also a cost — a large cookie is re-sent with every image and API call on the page.

Why tokens do not belong in localStorage

This is the part with real consequences.

Any JavaScript running on your page can read localStorage. That includes injected script — a cross-site scripting flaw, or a compromised third-party dependency. If an authentication token is there, it can be read and sent elsewhere, and the attacker is now logged in as your user.

A cookie marked HttpOnly cannot be read by JavaScript at all. Script can still cause requests to be made, but it cannot steal the token itself, which meaningfully limits the damage.

So the practical guidance: authentication tokens belong in HttpOnly, Secure, SameSite cookies set by the server. localStorage is for non-sensitive client state — theme, language, draft text, UI preferences.

Plenty of tutorials store JWTs in localStorage because it is simpler. Know that it is a trade-off with a known failure mode, and be able to say so in an interview — see API authentication explained.

Practical notes

Storage is per origin. Scheme, domain and port together — so localhost:3000 and localhost:5000 have entirely separate storage, which explains a surprising amount of local development confusion.

It can fail. In private browsing modes or when the quota is full, setItem throws. If your app cannot start without it, wrap it:

try {
  localStorage.setItem("theme", "dark");
} catch (e) {
  // quota exceeded or storage unavailable
}

It is synchronous. Reading or writing blocks the main thread, so avoid large values or writes inside a scroll handler.

Namespace your keysmyapp:theme rather than theme — since everything on the origin shares one space and collisions are silent.

Frequently Asked Questions

What is the difference between localStorage and sessionStorage? Lifetime. localStorage persists until explicitly cleared and is shared across tabs on the same origin. sessionStorage is cleared when the tab closes and is not shared between tabs.
How much can I store? Roughly 5 MB per origin for localStorage, varying by browser. Cookies are limited to about 4 KB each. For larger structured data, IndexedDB is the appropriate tool.
Why did my object become [object Object]? You stored it without JSON.stringify. Storage only holds strings, so the object was converted using its default string representation. Stringify on write and parse on read.
Is localStorage safe for JWT tokens? Not ideal. Any script on the page can read it, so a cross-site scripting flaw or a compromised dependency exposes the token. HttpOnly cookies set by the server are the safer default.
Does localStorage work offline? Yes, it is entirely client-side and requires no network. That makes it useful for caching preferences and draft data, though a service worker is the proper tool for full offline support.