Home JavaScript LocalStorage vs SessionStorage Explained — Store, Read and Clear Browser Data

LocalStorage vs SessionStorage Explained — Store, Read and Clear Browser Data

In Plain English 🔥
Imagine your browser is a desk at work. LocalStorage is like a personal drawer in that desk — you lock it at the end of the day, come back the next morning, and everything is still there. SessionStorage is like a sticky note on your monitor — super useful while you're working, but when you shut down your computer and come back, the note is gone. Both let your browser remember things about you, but one remembers forever and one forgets when you leave.
⚡ Quick Answer
Imagine your browser is a desk at work. LocalStorage is like a personal drawer in that desk — you lock it at the end of the day, come back the next morning, and everything is still there. SessionStorage is like a sticky note on your monitor — super useful while you're working, but when you shut down your computer and come back, the note is gone. Both let your browser remember things about you, but one remembers forever and one forgets when you leave.

Every time you visit a website, close the tab, and come back to find your shopping cart still full or your dark-mode setting still on — that is browser storage doing its job quietly in the background. Without it, every page refresh would wipe the slate clean and you'd have to log back in, re-select your preferences, and re-fill your forms from scratch. Browser storage is what turns a website from a goldfish-memory stranger into a place that actually remembers you.

Before LocalStorage and SessionStorage existed, developers had to shove everything into cookies — tiny text files that were sent back and forth to the server on every single request. That was wasteful, slow, and had a laughably small size limit. The Web Storage API (which gives us LocalStorage and SessionStorage) was introduced to solve that: give the browser a proper place to hold onto data locally, without pestering the server every five seconds.

By the end of this article you'll know exactly how to save data to both LocalStorage and SessionStorage, read it back, update it, and delete it. You'll understand which one to reach for and when, you'll avoid the mistakes that trip up most beginners, and you'll be able to answer the questions interviewers love to ask about browser storage.

What Is the Web Storage API and Why Does It Exist?

The Web Storage API is a set of tools built into every modern browser that lets your JavaScript code save information directly inside the user's browser. No server needed. No databases. Just your code and the browser.

It lives on the global window object, which means you can access it anywhere in your JavaScript without importing anything. window.localStorage and window.sessionStorage are always available — though in practice you just type localStorage and sessionStorage because the browser assumes window for you.

All data is stored as key-value pairs, exactly like a labelled filing cabinet. The key is the label on the folder, and the value is the document inside it. You decide both. The critical rule: both the key and the value MUST be strings. That means if you want to store a number or an object, you need to convert it to a string first — we'll cover exactly how to do that.

LocalStorage persists until the user or your code explicitly clears it. It survives the browser being closed, the computer being restarted, anything short of the user clearing their browser data. SessionStorage is scoped to a single browser tab and a single session — the moment that tab is closed, the data is gone. Same origin, different tab? Different SessionStorage. That distinction matters enormously in real apps.

webStorageBasics.js · JAVASCRIPT
1234567891011121314151617181920212223242526
// ─────────────────────────────────────────────
// Web Storage API — Getting Your Bearings
// Run this in your browser's DevTools console
// ─────────────────────────────────────────────

// Check that localStorage is available in this browser
if (typeof localStorage !== 'undefined') {
  console.log('localStorage is supported ✅');
} else {
  console.log('localStorage is NOT supported ❌');
}

// localStorage and sessionStorage live on the window object
// These two lines do exactly the same thing:
console.log(window.localStorage === localStorage); // true — same reference
console.log(window.sessionStorage === sessionStorage); // true — same reference

// Both storages have the same API methods:
// .setItem(key, value)  → save data
// .getItem(key)         → read data
// .removeItem(key)      → delete one item
// .clear()              → delete everything
// .length               → how many items are stored

console.log('localStorage item count:', localStorage.length);
console.log('sessionStorage item count:', sessionStorage.length);
▶ Output
localStorage is supported ✅
true
true
localStorage item count: 0
sessionStorage item count: 0
🔥
Where to practise this:Open any webpage, press F12 (or Cmd+Option+I on Mac) to open DevTools, click the 'Console' tab, and type any of these examples directly. You can also inspect what's stored by clicking the 'Application' tab and looking under 'Storage' on the left sidebar.

Saving, Reading, Updating and Deleting Data — The Full CRUD Lifecycle

There are four things you'll do with browser storage all the time: Create (save), Read (retrieve), Update (overwrite), and Delete (remove). Let's walk through all four with a realistic example — saving a user's display name and theme preference.

setItem(key, value) saves a piece of data. If an item with that key already exists, it silently overwrites it — that's your update operation covered for free.

getItem(key) reads the data back. If the key doesn't exist, it returns null — not undefined, not an error — null. Always handle that case, or your app will break the first time a new user visits.

removeItem(key) deletes one specific item. clear() wipes everything stored by your origin in that storage type. Be careful with clear() — it nukes all keys, not just the ones your app set.

The biggest beginner trip-up: storing objects and arrays. setItem converts whatever you pass to a string, so if you do localStorage.setItem('user', {name: 'Alex'}), you get the useless string '[object Object]' stored. The fix is JSON.stringify() before saving and JSON.parse() after reading — always.

storageCRUD.js · JAVASCRIPT
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
// ─────────────────────────────────────────────
// Full CRUD with LocalStorage
// Realistic example: saving user preferences
// ─────────────────────────────────────────────

// ── CREATE (Save) ─────────────────────────────

// Saving a simple string — no conversion needed
localStorage.setItem('username', 'Alex');
localStorage.setItem('theme', 'dark');

// Saving a number — must convert to string
localStorage.setItem('loginCount', String(5));

// Saving an object — MUST use JSON.stringify()
const userPreferences = {
  language: 'en',
  notifications: true,
  fontSize: 16
};
localStorage.setItem('preferences', JSON.stringify(userPreferences));

console.log('Items saved! Total stored:', localStorage.length);


// ── READ (Retrieve) ───────────────────────────

const storedUsername = localStorage.getItem('username');
console.log('Username:', storedUsername); // 'Alex'

// Reading a key that doesn't exist returns null — always guard against this
const storedAvatar = localStorage.getItem('avatar');
console.log('Avatar (not set):', storedAvatar); // null

// Reading an object — MUST use JSON.parse() to convert back from string
const rawPreferences = localStorage.getItem('preferences');
const parsedPreferences = JSON.parse(rawPreferences);
console.log('Font size:', parsedPreferences.fontSize); // 16
console.log('Notifications on?', parsedPreferences.notifications); // true

// Safe pattern: handle the case where the item might not exist yet
const safePreferences = JSON.parse(localStorage.getItem('preferences')) || {};
console.log('Safe read result:', safePreferences);


// ── UPDATE (Overwrite) ────────────────────────

// setItem on an existing key simply overwrites it — no separate update method
localStorage.setItem('theme', 'light'); // was 'dark', now 'light'
console.log('Updated theme:', localStorage.getItem('theme')); // 'light'

// Updating a nested property inside a stored object:
const currentPrefs = JSON.parse(localStorage.getItem('preferences'));
currentPrefs.fontSize = 18; // change just this one property
localStorage.setItem('preferences', JSON.stringify(currentPrefs)); // save it back
console.log('Updated font size:', JSON.parse(localStorage.getItem('preferences')).fontSize); // 18


// ── DELETE (Remove) ───────────────────────────

// Remove one specific item
localStorage.removeItem('loginCount');
console.log('After removeItem, loginCount:', localStorage.getItem('loginCount')); // null

// Remove EVERYTHING stored by your site (use with caution!)
// localStorage.clear();
// console.log('After clear(), item count:', localStorage.length); // 0
▶ Output
Items saved! Total stored: 4
Username: Alex
Avatar (not set): null
Font size: 16
Notifications on? true
Safe read result: { language: 'en', notifications: true, fontSize: 16 }
Updated theme: light
Updated font size: 18
After removeItem, loginCount: null
⚠️
Watch Out: The JSON.stringify / JSON.parse RuleForgetting JSON.stringify when saving an object is the #1 beginner mistake. You won't get an error — you'll silently store the useless string '[object Object]'. Always stringify before saving objects or arrays, and always parse after reading them back.

SessionStorage — Same API, Completely Different Lifespan

SessionStorage uses the exact same methods as LocalStorage — setItem, getItem, removeItem, clear. You can copy-paste code between them just by swapping the variable name. The difference is entirely about lifespan and scope.

A 'session' in browser terms means one tab, open and alive. The moment that tab is closed — not refreshed, not navigated away from, but actually closed — the session ends and all SessionStorage data for that tab is wiped. If a user opens your site in two tabs, each tab has its own completely separate SessionStorage. They cannot see each other's data.

This makes SessionStorage perfect for things that should be temporary and tab-specific: a multi-step form where you don't want data leaking between two open copies of the same form, a temporary draft the user hasn't committed yet, or step-by-step wizard data that should reset if the user opens a fresh tab.

LocalStorage, by contrast, is shared across all tabs from the same origin. Open your site in five tabs — they all read and write to the same LocalStorage. That's powerful for syncing things like login state or theme preferences, but it means changes in one tab immediately affect all others.

sessionStorageExample.js · JAVASCRIPT
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657
// ─────────────────────────────────────────────
// SessionStorage — Tab-Scoped Temporary Storage
// Great example: saving a multi-step form draft
// ─────────────────────────────────────────────

// ── Saving form progress step by step ────────

// User fills in Step 1 of a signup form
const stepOneData = {
  firstName: 'Jordan',
  lastName: 'Rivera',
  email: 'jordan@example.com'
};
sessionStorage.setItem('signupStep1', JSON.stringify(stepOneData));
console.log('Step 1 saved to sessionStorage');

// User moves to Step 2
const stepTwoData = {
  plan: 'pro',
  billingCycle: 'annual'
};
sessionStorage.setItem('signupStep2', JSON.stringify(stepTwoData));
console.log('Step 2 saved to sessionStorage');

// ── Reading it back on page load ──────────────

// On any page reload within the same tab, this data is still here
const savedStep1 = JSON.parse(sessionStorage.getItem('signupStep1'));
if (savedStep1) {
  console.log('Resuming form for:', savedStep1.firstName, savedStep1.lastName);
} else {
  console.log('No saved form data — starting fresh');
}

// ── Checking the difference in scope ─────────

// Save something to BOTH storages so we can compare their lifespans
localStorage.setItem('persistentNote', 'I survive tab closes and restarts');
sessionStorage.setItem('temporaryNote', 'I disappear when this tab closes');

console.log('localStorage note:', localStorage.getItem('persistentNote'));
console.log('sessionStorage note:', sessionStorage.getItem('temporaryNote'));

// Now close this tab and open a new one to the same page...
// localStorage.getItem('persistentNote')  → still 'I survive tab closes and restarts'
// sessionStorage.getItem('temporaryNote') → null (it's gone)

// ── Cleanup after form submission ─────────────
function handleFormSubmissionSuccess() {
  // Once the user successfully submits, clean up the drafts
  sessionStorage.removeItem('signupStep1');
  sessionStorage.removeItem('signupStep2');
  console.log('Form submitted — draft data cleared from sessionStorage');
}

handleFormSubmissionSuccess();
console.log('Step1 after cleanup:', sessionStorage.getItem('signupStep1')); // null
▶ Output
Step 1 saved to sessionStorage
Step 2 saved to sessionStorage
Resuming form for: Jordan Rivera
localStorage note: I survive tab closes and restarts
sessionStorage note: I disappear when this tab closes
Form submitted — draft data cleared from sessionStorage
Step1 after cleanup: null
⚠️
Pro Tip: SessionStorage for Sensitive Temporary DataIf you need to hold onto something sensitive (like a one-time token or a temporary auth code) only for the duration of a user's interaction with a single tab, SessionStorage is safer than LocalStorage because it auto-clears when the tab closes — reducing the window of exposure.

Listening for Storage Changes Across Tabs with the storage Event

Here's something most beginner tutorials skip entirely: LocalStorage fires a built-in browser event called storage whenever it changes — but only in OTHER tabs, not the one that made the change. This sounds weird at first, but it's actually brilliant. It lets you keep multiple open tabs in sync without polling a server.

Imagine a user has your app open in two tabs. They switch to dark mode in Tab A. Tab B fires the storage event and can immediately update its own UI to match — no page refresh, no server call, no complex setup.

This only works for LocalStorage, not SessionStorage (since SessionStorage is already isolated per-tab, there's nothing to sync). And remember: the event fires in the OTHER tabs, not the one that triggered the change. If you want to react in the same tab, just handle it directly in your code after calling setItem.

This pattern is genuinely useful for things like: keeping a login/logout state in sync across tabs, syncing a shopping cart count in the header, or broadcasting a notification that the user's session has expired.

storageSyncAcrossTabs.js · JAVASCRIPT
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647
// ─────────────────────────────────────────────
// The 'storage' Event — Syncing Tabs in Real Time
//
// HOW TO TEST THIS:
// 1. Open your site in Tab A and Tab B
// 2. Run this code in BOTH tabs
// 3. In Tab A, run: localStorage.setItem('cartCount', '3')
// 4. Watch Tab B's console react automatically
// ─────────────────────────────────────────────

// Listen for any localStorage change that originates from ANOTHER tab
window.addEventListener('storage', function(storageEvent) {

  // storageEvent contains useful info about what changed:
  console.log('Storage changed in another tab!');
  console.log('Key that changed:', storageEvent.key);        // e.g. 'cartCount'
  console.log('Old value:', storageEvent.oldValue);          // e.g. null
  console.log('New value:', storageEvent.newValue);          // e.g. '3'
  console.log('Which storage:', storageEvent.storageArea);   // localStorage object
  console.log('Origin:', storageEvent.url);                  // URL of the tab that changed it

  // React to a specific key changing
  if (storageEvent.key === 'cartCount') {
    const newCount = parseInt(storageEvent.newValue, 10);
    updateCartBadge(newCount); // call your UI update function
  }

  // React to the user logging out in another tab
  if (storageEvent.key === 'authToken' && storageEvent.newValue === null) {
    console.log('User logged out in another tab — redirecting to login page...');
    // window.location.href = '/login';
  }
});

// Simulated UI update function
function updateCartBadge(count) {
  console.log(`Cart badge updated to: ${count} item(s)`);
  // In a real app you'd do something like:
  // document.getElementById('cart-badge').textContent = count;
}

// Simulating a change from another tab (for demonstration):
// In the real scenario, Tab A does this:
localStorage.setItem('cartCount', '3');
// Tab B's 'storage' listener fires automatically — Tab A's does NOT.

console.log('This tab set cartCount. The storage event fires in OTHER tabs, not this one.');
▶ Output
This tab set cartCount. The storage event fires in OTHER tabs, not this one.

--- In the OTHER tab (Tab B), you would see: ---
Storage changed in another tab!
Key that changed: cartCount
Old value: null
New value: 3
Which storage: Storage {}
Origin: https://yoursite.com/page.html
Cart badge updated to: 3 item(s)
🔥
Interview Gold: The storage EventMost candidates have never heard of the storage event — knowing it exists and explaining the cross-tab sync use case will genuinely impress an interviewer. It shows you've thought beyond basic get/set and understand real-world multi-tab user behaviour.
Feature / AspectLocalStorageSessionStorage
LifespanPersists forever until explicitly cleared by code or the userCleared automatically when the tab is closed
ScopeShared across ALL tabs and windows from the same originIsolated to the exact tab that created it — not shared
Storage limit~5–10 MB depending on the browser~5 MB depending on the browser
Survives page refresh?YesYes
Survives browser restart?YesNo
Accessible from other tabs?Yes — all same-origin tabs share itNo — each tab has its own private copy
Fires storage event?Yes — notifies OTHER tabs of changesNo — tab-isolated so no cross-tab events
Sent to server?Never — purely client-sideNever — purely client-side
Best used forUser preferences, theme, remembered login name, cached dataMulti-step form drafts, temporary auth tokens, wizard state
JavaScript APIIdentical — setItem, getItem, removeItem, clear, lengthIdentical — same API as LocalStorage

🎯 Key Takeaways

  • LocalStorage survives forever — tab closes, browser restarts, computer reboots — until your code or the user explicitly clears it. SessionStorage dies when the tab closes, full stop.
  • All stored values must be strings. Save objects and arrays with JSON.stringify() and read them back with JSON.parse() — skip this and you'll silently store '[object Object]' with no error to warn you.
  • LocalStorage is shared across all tabs from the same origin. SessionStorage is completely isolated to the individual tab that created it — even two tabs on the same page have separate SessionStorages.
  • The storage event lets LocalStorage changes in one tab automatically notify all other same-origin tabs — use this to sync UI state like cart counts or theme preferences without touching a server.

⚠ Common Mistakes to Avoid

  • Mistake 1: Storing objects without JSON.stringify — You call localStorage.setItem('user', {name: 'Alex'}) and later read back '[object Object]' instead of real data — Fix: Always wrap objects and arrays in JSON.stringify() before saving, and always call JSON.parse() when reading them back. Example: localStorage.setItem('user', JSON.stringify({name: 'Alex'})) and const user = JSON.parse(localStorage.getItem('user')).
  • Mistake 2: Not handling the null case when reading — You call JSON.parse(localStorage.getItem('settings')) on a first-time visitor and get a TypeError because JSON.parse(null) returns null in some environments or throws in others — Fix: Always use a fallback: const settings = JSON.parse(localStorage.getItem('settings')) || {}. The || {} guard means new users get a safe empty object instead of a crash.
  • Mistake 3: Storing sensitive data like passwords or full auth tokens — localStorage is accessible to any JavaScript running on your page, including third-party scripts. If your site has an XSS vulnerability, an attacker can read everything in localStorage — Fix: Never store passwords, credit card numbers, or long-lived auth tokens in localStorage. Use HttpOnly cookies (set by the server) for sensitive session credentials — they are inaccessible to JavaScript entirely.

Interview Questions on This Topic

  • QWhat is the difference between LocalStorage and SessionStorage, and can you give a concrete example of when you would choose one over the other?
  • QWhat happens if you try to store a JavaScript object directly in LocalStorage without any conversion — what value actually gets stored and why?
  • QWhat is the storage event, which storage type triggers it, and why does it only fire in tabs other than the one that made the change?

Frequently Asked Questions

Is LocalStorage safe to store user passwords or tokens?

No — never store passwords in LocalStorage. It's readable by any JavaScript on your page, which makes it vulnerable to XSS attacks. For sensitive session tokens, use HttpOnly cookies set by your server instead — those cannot be accessed by JavaScript at all.

Does LocalStorage data expire automatically?

No, LocalStorage has no built-in expiry mechanism. It persists until your code calls removeItem or clear, or the user manually clears their browser data. If you need data to expire, you have to implement it yourself by storing a timestamp alongside the data and checking it on read.

Why does reading from LocalStorage give me null even though I saved data there?

The most common cause is a key mismatch — setItem and getItem are case-sensitive, so 'UserName' and 'username' are different keys. The second cause is being on a different origin (different domain or protocol), since LocalStorage is sandboxed per origin. Check the Application tab in DevTools to see exactly what keys are stored and their exact spelling.

🔥
TheCodeForge Editorial Team Verified Author

Written and reviewed by senior developers with real-world experience across enterprise, startup and open-source projects. Every article on TheCodeForge is written to be clear, accurate and genuinely useful — not just SEO filler.

← PreviousFetch API and AJAX in JavaScriptNext →MutationObserver API
Forged with 🔥 at TheCodeForge.io — Where Developers Are Forged