DEV Community

Snappy Tools
Snappy Tools

Posted on

localStorage vs sessionStorage vs Cookies: When to Use Each

Three browser storage mechanisms, each with different lifetimes, scopes, and appropriate uses. Here's when to reach for each.

Overview

Feature localStorage sessionStorage Cookies
Capacity ~5–10 MB ~5 MB ~4 KB
Expires Never (until cleared) Tab/session close Set explicitly
Sent with requests No No Yes (automatically)
Accessible from JS Yes Yes Yes (unless HttpOnly)
Scope Domain Tab + domain Domain (configurable)

localStorage

Stores data with no expiry. Persists until the user clears it or the site clears it.

// Write
localStorage.setItem('theme', 'dark');
localStorage.setItem('user', JSON.stringify({ id: 1, name: 'Alice' }));

// Read
const theme = localStorage.getItem('theme');  // 'dark'
const user = JSON.parse(localStorage.getItem('user'));

// Delete
localStorage.removeItem('theme');

// Clear all
localStorage.clear();

// Check existence
if (localStorage.getItem('onboarded') === null) {
  // first visit
}
Enter fullscreen mode Exit fullscreen mode

All values are strings. Use JSON.stringify / JSON.parse for objects and arrays.

Use for: user preferences (theme, language, layout), non-sensitive app state, data that should survive browser restarts.

Don't use for: authentication tokens, sensitive data. localStorage is accessible from any JavaScript on the page — an XSS vulnerability can read everything.

sessionStorage

Same API as localStorage, but scoped to the current tab and cleared when the tab closes.

sessionStorage.setItem('cart', JSON.stringify(cartItems));
const cart = JSON.parse(sessionStorage.getItem('cart'));
Enter fullscreen mode Exit fullscreen mode

Each tab gets its own sessionStorage. Opening the same URL in two tabs gives each tab independent storage — data in one tab is not visible in the other.

Use for: form state across multiple steps (wizard forms), tab-specific UI state, temporary data that shouldn't persist across sessions.

Don't use for: data that should survive refreshes (it does survive refreshes, but NOT tab close), data shared across tabs.

Cookies

Cookies are old but have specific advantages: they're automatically sent with every HTTP request to the domain, and can be marked HttpOnly to prevent JavaScript access.

// Set a cookie (JavaScript)
document.cookie = 'visited=true; max-age=86400; path=/';

// Read cookies (awkward — they're all in one string)
document.cookie  // "visited=true; theme=dark; lang=en"

// Parsing requires manual work or a library:
function getCookie(name) {
  return document.cookie.split('; ')
    .find(row => row.startsWith(name + '='))
    ?.split('=')[1];
}

// Delete (set expired)
document.cookie = 'visited=; max-age=0; path=/';
Enter fullscreen mode Exit fullscreen mode

Cookie attributes that matter:

Set-Cookie: token=abc123; 
  HttpOnly;           ← JS cannot read this cookie
  Secure;             ← only sent over HTTPS  
  SameSite=Strict;    ← not sent on cross-site requests
  Max-Age=86400;      ← expires in 24 hours
  Path=/              ← sent for all paths
Enter fullscreen mode Exit fullscreen mode

Use for: session tokens (always set HttpOnly + Secure + SameSite), any data the server needs with each request, cross-subdomain data sharing with Domain=.example.com.

Don't use for: large data (4KB limit), client-side-only preferences (unnecessary server overhead), anything that doesn't need to be sent to the server.

Authentication tokens: the right approach

This is where developers make the most consequential storage decision.

JWTs in localStorage: convenient, but vulnerable to XSS. If any third-party script, browser extension, or injected code can run on your page, it can steal the token.

JWTs in HttpOnly cookies: the server sets the cookie with HttpOnly, JavaScript cannot read it, and XSS attacks can't steal it. The cookie is sent automatically with every request. This is the more secure pattern.

// Server sets the cookie (Node.js / Express example)
res.cookie('token', jwt, {
  httpOnly: true,
  secure: true,
  sameSite: 'strict',
  maxAge: 24 * 60 * 60 * 1000  // 24 hours
});
Enter fullscreen mode Exit fullscreen mode

The tradeoff: HttpOnly cookies require CSRF protection (the SameSite attribute handles most cases). localStorage tokens are easier to implement but have a larger XSS attack surface.

For most applications: HttpOnly cookies for auth tokens, localStorage for non-sensitive preferences.

Storage events

localStorage changes in one tab fire an event in other tabs from the same origin:

// In tab A, tab B will receive this event when tab A calls localStorage.setItem
window.addEventListener('storage', (event) => {
  console.log(event.key);      // 'theme'
  console.log(event.newValue); // 'dark'
  console.log(event.oldValue); // 'light'
});
Enter fullscreen mode Exit fullscreen mode

This doesn't fire in the tab that made the change — only in other tabs. Useful for syncing state across tabs.

Checking storage availability

localStorage may be unavailable in private browsing on some browsers or when storage quota is exceeded:

function storageAvailable(type) {
  try {
    const storage = window[type];
    const x = '__storage_test__';
    storage.setItem(x, x);
    storage.removeItem(x);
    return true;
  } catch {
    return false;
  }
}

if (storageAvailable('localStorage')) {
  localStorage.setItem('setting', 'value');
} else {
  // fall back to a cookie or in-memory variable
}
Enter fullscreen mode Exit fullscreen mode

Quick decision guide

  • Non-sensitive preferences, survives browser close → localStorage
  • Temporary state for current tab only → sessionStorage
  • Auth token, needs to be secure → HttpOnly cookie
  • Sent to server automatically → Cookie
  • Data shared across subdomains → Cookie with Domain=.example.com
  • More than 4KB → localStorage or sessionStorage (not cookies)

The key differences: localStorage outlives sessions and tabs; sessionStorage scopes to the current tab; cookies travel to the server and can be made inaccessible to JavaScript. Reach for the mechanism that matches the data's lifetime and who needs to read it.

Top comments (0)