LocalStorage vs SessionStorage Explained — Store, Read and Clear Browser Data
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.
// ───────────────────────────────────────────── // 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);
true
true
localStorage item count: 0
sessionStorage item count: 0
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.
// ───────────────────────────────────────────── // 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
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
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.
// ───────────────────────────────────────────── // 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
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
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.
// ───────────────────────────────────────────── // 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.');
--- 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)
| Feature / Aspect | LocalStorage | SessionStorage |
|---|---|---|
| Lifespan | Persists forever until explicitly cleared by code or the user | Cleared automatically when the tab is closed |
| Scope | Shared across ALL tabs and windows from the same origin | Isolated 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? | Yes | Yes |
| Survives browser restart? | Yes | No |
| Accessible from other tabs? | Yes — all same-origin tabs share it | No — each tab has its own private copy |
| Fires storage event? | Yes — notifies OTHER tabs of changes | No — tab-isolated so no cross-tab events |
| Sent to server? | Never — purely client-side | Never — purely client-side |
| Best used for | User preferences, theme, remembered login name, cached data | Multi-step form drafts, temporary auth tokens, wizard state |
| JavaScript API | Identical — setItem, getItem, removeItem, clear, length | Identical — 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.
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.