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
// 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"JSON.stringify before saving and parse with JSON.parse after reading.storing-objects.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
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
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 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
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
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
HttpOnlycookies 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 |
|---|---|
| Save a string value under a key |
| Read a value, or |
| Delete a single key |
| Delete every key for this origin |
| Get the key name at a given index (rarely used) |
| Number of stored keys |