HTMLWeb Storage (localStorage)

Web Storage: localStorage & sessionStorage

The Web Storage API gives every page a simple key-value store built into the browser — no server, no cookies, no library required. It comes in two flavors, localStorage and sessionStorage, which share the same API but differ in how long the data survives.

localStorage vs sessionStorage

localStorage

sessionStorage

Lifetime

Persists until explicitly cleared

Cleared when the tab is closed

Shared across tabs?

Yes, same origin

No — each tab gets its own store

Typical use

User preferences, cached data, "remember me" flags

Multi-step form progress, per-tab state

Capacity

~5–10MB per origin (browser dependent)

~5–10MB per origin (browser dependent)

Basic API: setItem, getItem, removeItem

Both storage objects share the exact same methods.

web-storage-basics.js

JS
// Save a value
localStorage.setItem('theme', 'dark')

// Read it back
console.log(localStorage.getItem('theme')) // "dark"

// Remove a single key
localStorage.removeItem('theme')

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

// sessionStorage works identically
sessionStorage.setItem('draft-id', '42')
console.log(sessionStorage.getItem('draft-id')) // "42"
Values are always strings
Every value stored is coerced to a string. To store objects or arrays, serialize with JSON.stringify before saving and parse with JSON.parse after reading.

storing-objects.js

JS
const cart = { items: ['mug', 'sticker'], total: 24.5 }

localStorage.setItem('cart', JSON.stringify(cart))

const restored = JSON.parse(localStorage.getItem('cart'))
console.log(restored.items) // ['mug', 'sticker']
Checking for a Missing Key

getItem returns null, not undefined, for a key that was never set — always guard against that before parsing JSON.

null-check.js

JS
const raw = localStorage.getItem('cart')
const cart = raw ? JSON.parse(raw) : { items: [], total: 0 }
The storage Event for Cross-Tab Sync

When one tab changes localStorage, every other open tab on the same origin receives a storage event on the{' '} window object — but the tab that made the change does not. This makes it possible to keep multiple tabs in sync (e.g. logging out everywhere when the user logs out in one tab).

storage-event.js

JS
window.addEventListener('storage', (event) => {
  console.log('Key changed:', event.key)
  console.log('Old value:', event.oldValue)
  console.log('New value:', event.newValue)

  if (event.key === 'logged-out' && event.newValue === 'true') {
    window.location.reload()
  }
})

// In another tab:
localStorage.setItem('logged-out', 'true')
(In the OTHER open tab, after the change above)
Key changed: logged-out
Old value: null
New value: true
sessionStorage does not fire cross-tab events
Because sessionStorage is isolated per tab, the storage event mechanism is really only useful for localStorage — there is nothing to sync between separate session stores.
Storage Limits

Both storages are capped, typically somewhere around 5–10MB per origin depending on the browser (there is no single spec-mandated number). Exceeding the limit throws a QuotaExceededError.

handling-quota.js

JS
try {
  localStorage.setItem('big-blob', hugeString)
} catch (error) {
  if (error.name === 'QuotaExceededError') {
    console.warn('Storage is full — consider clearing old data')
  }
}
Security: Never Store Sensitive Data
Not encrypted, not private
Anything in localStorage or sessionStorage is stored in plain text on disk and readable by any JavaScript running on the page — including a malicious script injected via an XSS vulnerability. Never store passwords, raw session tokens, credit card numbers, or other sensitive data there.
  • Prefer HttpOnly cookies for authentication tokens — JavaScript cannot read those, which limits XSS exposure.

  • Treat anything in Web Storage as visible to any script on the page, and to anyone with physical/dev-tools access to the browser.

  • Clear sensitive-adjacent data (like a cached user profile) on logout.

Quick Reference

Method

Purpose

setItem(key, value)

Save a string value under a key

getItem(key)

Read a value, or null if the key is unset

removeItem(key)

Delete a single key

clear()

Delete every key for this origin

key(index)

Get the key name at a given index (rarely used)

.length

Number of stored keys