Three ways to store data in a browser, and they are all different
Every web developer runs into the same question eventually: where do I put this data so it survives a page refresh? The browser gives you three main options, and picking the wrong one leads to bugs that are annoying to track down.
localStorage, sessionStorage, and cookies all store data in the browser, but they behave differently in terms of lifetime, capacity, and what gets sent to the server. Understanding these differences saves you from the classic mistake of storing a user preference in a cookie and then wondering why it disappears after a month, or putting a large JSON blob in a cookie and watching your requests balloon in size.
localStorage sticks around until you clear it
localStorage is a key-value store that persists across browser sessions. You close the tab, close the browser, restart your computer, and the data is still there when you come back. The storage limit is typically around 5-10 MB per origin, which is plenty for configuration, history, and small datasets.
// Save a value
localStorage.setItem("theme", "dark");
// Read it back
const theme = localStorage.getItem("theme");
// Remove it
localStorage.removeItem("theme");
// Clear everything for this origin
localStorage.clear();
The API is synchronous and simple. You store strings, and you get strings back. If you need to store an object, you serialize it with JSON.stringify() and parse it back with JSON.parse().
const history = [
{ url: "https://example.com/stream.m3u8", date: "2026-06-23" },
{ url: "https://example.com/live.m3u8", date: "2026-06-22" },
];
localStorage.setItem("playbackHistory", JSON.stringify(history));
At Anoiona Tools, we use localStorage for playback history in the M3U8 player, theme preference, and language selection. These are things that should persist between visits but do not need to be synchronized across devices.
One thing to watch out for: localStorage is synchronous and blocking. If you write a large amount of data to it, the main thread pauses. For most use cases this is not noticeable, but if you are storing megabytes of data, it can cause a visible hitch.
sessionStorage dies when the tab closes
sessionStorage works like localStorage but with one critical difference: it is scoped to the current tab and cleared when the tab closes. Open the same site in two tabs, and each tab gets its own independent sessionStorage.
sessionStorage.setItem("currentStep", "3");
// Refresh the page, the value is still there
// Close the tab, the value is gone
This makes sessionStorage useful for multi-step forms, wizard flows, or temporary state that should not persist. If a user starts filling out a form, accidentally closes the tab, and comes back, they get a fresh start rather than a confusing half-filled state.
sessionStorage has the same storage limit and API as localStorage. The only difference is the lifetime and the per-tab isolation.
Most developers reach for localStorage by default and forget that sessionStorage exists. It is worth considering whether your data actually needs to survive a tab close. If it does not, sessionStorage is the cleaner choice because it does not accumulate over time.
Cookies are for the server
Cookies are fundamentally different from localStorage and sessionStorage because they are automatically sent with every HTTP request to the server. This is their primary purpose: letting the server know something about the client.
// Set a cookie that expires in 7 days
document.cookie = "session=abc123; max-age=604800; path=/; SameSite=Lax";
Cookies have a much smaller storage limit, typically around 4 KB per cookie. They can be set with an expiration date, a path, a domain, and security flags like HttpOnly, Secure, and SameSite.
The HttpOnly flag is particularly important: it prevents JavaScript from reading the cookie, which protects against cross-site scripting attacks. Authentication tokens should almost always be HttpOnly cookies.
Set-Cookie: token=xyz789; HttpOnly; Secure; SameSite=Strict; Path=/
For session management and authentication, cookies are the right tool. The server sets the cookie, and the browser sends it back with every request. The server can validate the session without any JavaScript involvement.
For client-side state that the server does not need to know about, cookies are the wrong tool. Every cookie you set adds to the size of every HTTP request to that domain. If you store a 3 KB JSON blob in a cookie, every image, stylesheet, and API call from that page carries that 3 KB in the request headers.
A quick decision guide
Use localStorage when:
- The data should persist across browser sessions.
- The data is only needed by JavaScript, not by the server.
- You need more than 4 KB of storage.
- Examples: user preferences, playback history, draft content.
Use sessionStorage when:
- The data should only live for the current tab session.
- You want isolation between tabs.
- Examples: form state, temporary filters, wizard progress.
Use cookies when:
- The server needs the data on every request.
- You are storing authentication tokens or session identifiers.
- The data is small (under 4 KB).
- Examples: session IDs, CSRF tokens, language preference sent to the server.
Do not use cookies for large client-side data. Do not use localStorage for authentication tokens that need HttpOnly protection. Do not use sessionStorage for data that should survive a browser restart.
Storage events and cross-tab communication
One feature of localStorage that is often overlooked is the storage event. When one tab changes a localStorage value, other tabs on the same origin receive an event. This can be useful for synchronizing state across tabs without a server.
window.addEventListener("storage", (event) => {
if (event.key === "theme") {
applyTheme(event.newValue);
}
});
sessionStorage does not fire this event because each tab has its own isolated storage. Cookies do not fire it either, though you can poll for cookie changes if needed.
This cross-tab behavior can be both useful and surprising. If you change a value in one tab and expect the change to be invisible to other tabs, localStorage is the wrong choice. Use sessionStorage or a more scoped state management approach.
Clearing storage is the user’s right
Browsers let users clear site data from the settings. Users can clear localStorage, sessionStorage, cookies, IndexedDB, and cached files for a specific site or for all sites. This means your application should handle missing storage gracefully.
If you try to read from localStorage and the value is gone, fall back to a default. If sessionStorage is empty because the user opened a new tab, do not crash. Storage is a convenience, not a guarantee.
Some browsers also clear site data automatically after a period of inactivity. Safari is particularly aggressive about this. If your application depends on localStorage for critical state, consider whether that state can be reconstructed if storage is cleared.