cf.completefrontendCode editorOpen lab
THE JAVASCRIPT FIELD GUIDE

localStorage & sessionStorage

Learn the browser Storage API: saving strings with localStorage and sessionStorage, storing JSON safely, reacting to storage events, and knowing when not to use it.

You will be able to
  • 01
    Save and read small valuesUse setItem, getItem, removeItem, key(index), and length without wiping unrelated site data.
  • 02
    Store real data safelyRound-trip objects with JSON, handle corrupt values, and migrate versioned settings.
  • 03
    Choose the right browser storageExplain storage events, quotas, sync costs, security risks, and alternatives.

Two tiny browser shelves

Web Storage is a pair of simple browser APIs for saving small pieces of text on one origin: the exact combination of scheme, host, and port, such as https://example.com. The two shelves have the same methods but different lifetimes.

  • localStorage keeps values after reloads, browser restarts, and return visits until your code or the user removes them.
  • sessionStorage keeps values for one top-level tab. Reloading keeps them; closing the tab removes them. In most browsers, duplicating a tab starts with a copy.
Real-life analogyDrawers and sticky notes

Imagine each website origin gets its own tiny office. The office has a small labeled drawer for values that should still be there next week. Each open tab also has a sticky note on its own monitor. A second tab has its own sticky note, even though it works in the same office.

In real life: A labeled drawer in this website’s office
In JavaScript: localStorage: this origin’s long-lived key/value shelf
In real life: Only this office’s staff can open it
In JavaScript: Only pages from the same origin can read those keys
In real life: A sticky note on this tab’s monitor
In JavaScript: sessionStorage: per-tab data that survives reloads
In real life: Throw away the monitor note when the tab closes
In JavaScript: Closing the tab ends that session storage

Where the analogy stops: Real drawers can hold anything. Web Storage can hold only strings, is synchronous, and is not a safe place for secrets.

Use Web Storage for preferences, UI state, feature tour flags, and small drafts that only the browser needs. Do not treat it like a database, a secure vault, or a server communication channel.

setItem, getItem, keys and removal

INTERACTIVE

Both storage objects implement the same tiny interface. You write with setItem(key, value), read with getItem(key), remove one key with removeItem(key), inspect the number of stored keys with length, and ask for the key at an index with key(index). There is also clear(), but production code should use it carefully because it wipes every key for this origin, not only yours.

Prefer method calls over property access

localStorage.theme = "dark" may appear to work, but setItem("theme", "dark") is explicit, consistent with getItem, and avoids surprises with built-in property names.

Drawer inspector: real Storage on this site
The important operationsPop out in the code editor (opens in a new tab)JavaScript
const storage = localStorage; storage.setItem("count", 7);storage.setItem("darkMode", false);storage.setItem("profile", { name: "Ada" }); console.log(storage.getItem("count"));console.log(storage.getItem("darkMode"));console.log(storage.getItem("profile"));console.log(storage.getItem("missing"));
Live demo keys0 keys

No cf-demo keys are currently stored.

Try it yourself

Click a button to write demo keys for this origin only.

This playground uses real localStorage and sessionStorage for this origin. It namespaces every key with cf-demo: and removes those keys on Reset and unmount.

The important rule is that keys and values are strings. The browser applies String(value) before saving. A number comes back as "7", a boolean as "false", and a plain object as "[object Object]". Missing keys return null, which is why you will often see localStorage.getItem("tab") ?? "overview".

Real-life analogyA locker that only fits notes

Think of Web Storage as a locker that only fits written notes. Put object data into a JSON note first, then read and parse that note when you need it again.

In real life: A note fits in the locker
In JavaScript: A string such as dark
In real life: Write object data as a JSON note
In JavaScript: JSON.stringify(settings)
In real life: Read the JSON note back
In JavaScript: JSON.parse(saved)

Where the analogy stops: JSON handles plain data, not every JavaScript value. Functions, symbols, Maps, Sets, and Dates need special handling.

Storing JSON safely

INTERACTIVE

The fix for plain objects is the JSON lesson’s pattern: turn data into text with JSON.stringify, then turn the text back into data with JSON.parse. Because stored text can be missing, old, manually edited in DevTools, or left behind by an older version of your app, read it defensively with try/catch.

JSON lab: stringify, parse, recover, migrate
Safe JSON patternPop out in the code editor (opens in a new tab)JavaScript
function readJSON(key, fallback) {  const text = localStorage.getItem(key);  if (text === null) return fallback;  try {    return JSON.parse(text);  } catch {    return fallback;  }} const oldSettings = {  version: 1,  data: { theme: "dark", compact: true }};localStorage.setItem("cf-demo:settings", JSON.stringify(oldSettings)); const loaded = readJSON("cf-demo:settings", { version: 2, data: {} });const current = loaded.version === 1  ? { version: 2, data: { ...loaded.data, textSize: "normal" } }  : loaded; localStorage.setItem("cf-demo:settings", JSON.stringify(current));
Round tripmigrated
Migrated settings{"version":2,"data":{"theme":"dark","compact":true,"textSize":"normal"}}
Saved text{"version":2,"data":{"theme":"dark","compact":true,"textSize":"normal"}}
Date after JSONstring
Safe read result{"version":2,"data":{"theme":"dark","compact":true,"textSize":"normal"}}
Try it yourself

Version 1 data loaded safely, migrated to version 2, then saved back as JSON text.

The code shown uses localStorage in a browser; the testable model uses the same getItem/setItem shape in memory.

Versioning matters as soon as real users have saved data. Store an envelope like { version: 2, data: settings }. When you load an older version, migrate it to the newest shape before the rest of your app uses it. That keeps the messy compatibility code in one place.

Values before and after JSON
ValueStored JSON textWhat to remember
Plain object{"theme":"dark"}Use JSON.stringify before writing and JSON.parse after reading.
Missing keynull from getItemReturn a fallback instead of parsing null accidentally.
Corrupt text{brokenCatch JSON.parse errors and recover.
Date objectA string such as 2026-09-25T00:00:00.000ZJSON does not restore a real Date; recreate one if you need date methods.

The storage event

INTERACTIVE

When one document changes localStorage or sessionStorage, other same-origin documents can receive a storage event. The document that made the change does not receive its own event. That keeps it useful for cross-tab coordination, not as a replacement for your current component’s state update.

Real-life analogyA colleague shouts from another office

Think of changing the drawer and shouting “I changed the drawer!” down the hallway. Other rooms can react. Your own room already knows because it made the change.

In real life: You change the labeled drawer
In JavaScript: One tab calls localStorage.setItem
In real life: A colleague in another room hears the shout
In JavaScript: Another same-origin tab or iframe receives storage
In real life: You do not hear your own shout
In JavaScript: The writing document gets no event
In real life: The shout names what changed
In JavaScript: The event includes key, oldValue, newValue, url, and storageArea

Where the analogy stops: The event is not a reliable message bus for large data or high-frequency updates. Use dedicated channels when coordination becomes complex.

Storage event: another document hears the shout
Listener shapePop out in the code editor (opens in a new tab)JavaScript
window.addEventListener("storage", (event) => {  console.log(event.key);  console.log(event.oldValue);  console.log(event.newValue);  console.log(event.url);  console.log(event.storageArea === localStorage);}); localStorage.setItem("cf-demo:signal", "hello");
Other documentown events: 0
    Try it yourself

    The iframe reports changes because it is another same-origin document. This page's own storage event count is 0.

    The iframe uses predefined srcdoc code and validates messages by source. Open this lesson in a second tab to see a full tab-to-tab version too.

    If clear() is used, event.key is null. In this lesson’s demos we avoid localStorage.clear() and remove only keys that start with cf-demo:, because this website may have its own unrelated keys such as theme preferences.

    Pattern: remember the last tab

    STEP THROUGH

    A common use is remembering the last tab, panel, or filter a visitor chose. The pattern is deliberately small: read with a fallback, let the user choose, write the new string, then read it on the next visit. The execution below is recorded from a testable in-memory Storage model, because Node does not provide browser localStorage by default.

    Remember the last tab
    Step 0 of 7Ready
    Your turn: follow the blue line

    Step through a practical pattern: read a saved value with a fallback, let the user choose, write the new value, then restore it later.

    Running in
    1. script
    Next: line 1
    Click the blue line to take the next stepPop out in the code editor (opens in a new tab)JavaScript
    const fallback = "overview";let activeTab = storage.getItem("lastTab") ?? fallback; activeTab = "examples";storage.setItem("lastTab", activeTab); const nextVisit = storage.getItem("lastTab") ?? fallback;console.log(nextVisit);
    CallStoreChangeResultRun = next line. Ran = already executed.
    Recent returnsNothing yet. Start with the blue line.
    Choose the tab the visitor clicks on line 5

    Changing the choice creates a fresh recording. Predict the final log before stepping.

    A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

    Notice that storage is not the live source of truth for every click. Your UI state changes immediately, then you persist the latest choice. On page load, storage simply helps choose the initial state.

    Limits and when not to use it

    HONEST EDGES

    Web Storage is convenient, but it is intentionally small and simple. Browsers commonly allow around a few megabytes per origin, often around 5 MB, but the real limit varies. setItem can throw a QuotaExceededError DOMException when the browser refuses a write, so production code should catch it and degrade gracefully.

    Quota probe: writes can fail
    Production write shapePop out in the code editor (opens in a new tab)JavaScript
    try {  localStorage.setItem("cf-demo:settings", jsonText);} catch (error) {  // QuotaExceededError or storage unavailable: fall back gracefully.}
    Resulttemporary key

    Press Test to try one temporary demo write, then remove it immediately.

    Try it yourself

    Press Test to try one temporary demo write, then remove it immediately.

    This probe is intentionally small and cleans up after itself. Real quotas vary by browser, device, storage pressure, and privacy mode.
    When Web Storage is the wrong tool
    ConcernWhy it mattersBetter choice
    Secrets and tokensAny script running on the page can read Web Storage, so an XSS bug can steal it.Avoid storing secrets there; server-managed HttpOnly cookies are often safer for session secrets.
    Large or structured dataEverything is a string and the synchronous API can block the main thread.IndexedDB for records, indexes, blobs, and larger offline data.
    Server needs the valueWeb Storage is not sent with HTTP requests.Cookies for small server-needed values.
    WorkerslocalStorage and sessionStorage are Window APIs, not Worker APIs.IndexedDB or messages between the worker and window.
    Private browsingSome browsers clear data sooner or restrict persistence.Feature-detect, catch errors, and treat storage as optional.
    Security rule

    Never store passwords, raw access tokens, payment details, or other secrets in localStorage or sessionStorage. If malicious script runs on your page, it can read them.

    localStorage, sessionStorage, IndexedDB, or cookie?

    SORT IT

    The next lesson is IndexedDB, a real browser database. For now, keep the decision tree simple: Web Storage is for small client-only strings, cookies are for tiny values the server needs, and IndexedDB is for larger structured client data.

    Choose the storage shelf
    • Remember this site’s light/dark theme after the browser restarts
    • Keep a checkout wizard’s current step only for this tab
    • Store hundreds of offline notes with indexes and structured records
    • Send a tiny language preference with the next server request
    • Save an access token that would be dangerous if script could read it
    • Remember a product filter panel’s choices on this origin
    • Keep a preview mode separate in two same-site tabs
    • Cache large blobs or files for offline use
    Try it yourself
    0 of 8 correct

    Sort each realistic scenario into the storage option you would reach for first.

    Choose a category for every card. You can change an answer at any time; Reset clears them all.

    Common misconceptions

    • “It stores JavaScript values.” It stores strings. Use JSON for plain data and rebuild special types like Date deliberately.
    • “sessionStorage disappears on refresh.” It survives reloads. It disappears when that top-level tab session ends.
    • “Every tab shares sessionStorage.” Tabs on the same origin share localStorage, but each top-level tab gets its own sessionStorage area.
    • “The storage event tells my current page it wrote.” It fires in other same-origin documents, not the writer.
    • “It is safe because users cannot see it.” Users and scripts can inspect it. Never put secrets there.
    • “clear() is a harmless reset.” It wipes the whole storage area for the origin. Prefer removing your own namespaced keys.

    Practice exercises

    5 EXERCISES
    Exercise 1 · Warm-upPredict stored strings

    Predict the two console lines.

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    const storage = new Map();
    storage.set("count", String(3));
    storage.set("flag", String(false));
    console.log(storage.get("count") + 2);
    console.log(typeof storage.get("flag"));

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 2 · PracticeWrite a safe reader

      Read the helper and predict the two themes it prints.

      Starter codePop out in the code editor (opens in a new tab)JavaScript
      function readJSON(text, fallback) {
        if (text === null) return fallback;
        try {
          return JSON.parse(text);
        } catch {
          return fallback;
        }
      }
      console.log(readJSON('{"theme":"dark"}', {}).theme);
      console.log(readJSON('{broken', { theme: "light" }).theme);

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 3 · PracticeFind the event bug

        A developer writes a setting and waits for the same page’s storage listener to update the UI. Will that listener run in the writing page?

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 4 · PracticeRemember a last tab

          Predict the output of the small Map-backed Storage model.

          Starter codePop out in the code editor (opens in a new tab)JavaScript
          const storage = new Map();
          const fallback = "home";
          let tab = storage.get("tab") ?? fallback;
          tab = "settings";
          storage.set("tab", tab);
          console.log(storage.get("tab") ?? fallback);

          Answer, then press Check. Spacing and letter case don’t matter.

            Exercise 5 · ChallengeDesign a storage plan

            For a note-taking app, decide where to store: theme preference, current unsaved tab preview, hundreds of offline notes, and the signed-in session secret. Explain each choice.

              Check your understanding

              7 QUESTIONS
              Web Storage quiz · 7 questionsScore: first tries count
              1. Question 1 of 7What does getItem return when a key is missing?

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                const storage = new Map();
                const getItem = (key) => storage.has(key) ? storage.get(key) : null;
                console.log(getItem("missing"));

                Choose an answer to see the explanation.

              2. Question 2 of 7What does this print after storing a number?

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                const value = String(42);
                console.log(typeof value);
                console.log(value + 1);

                Choose an answer to see the explanation.

              3. Question 3 of 7Which value should you store for an object you want back later?

                Choose an answer to see the explanation.

              4. Question 4 of 7Where does a storage event fire after one document changes localStorage?

                Choose an answer to see the explanation.

              5. Question 5 of 7Which statement about sessionStorage is most accurate?

                Choose an answer to see the explanation.

              6. Question 6 of 7What should production code do around setItem when storage may be full?

                Choose an answer to see the explanation.

              7. Question 7 of 7Which storage is best for a large offline collection of structured records?

                Choose an answer to see the explanation.

              Key takeaways

              • localStorage is per-origin and long-lived; sessionStorage is per-origin and per-tab.
              • Keys and values are strings. Missing getItem results are null.
              • Use JSON, fallbacks, try/catch, and version migrations for real data.
              • The storage event fires in other same-origin documents, not the writer.
              • Catch quota failures, avoid secrets, and choose IndexedDB or cookies when they fit better.

              Final definition: Web Storage is a synchronous, string-only, per-origin browser key/value API for small client-side data that should live either across visits or within one tab session.

              Up next: IndexedDB.

              CompleteFrontend Clear concepts. Working examples.