MarzleyTech Learn

Home / Learn / JavaScript / Saving data in the browser: localStorage, sessionStorage, cookies and IndexedDB

Saving data in the browser: localStorage, sessionStorage, cookies and IndexedDB

Websites often need to remember things between visits: a shopping cart, dark mode, a draft message, the last lesson you opened (this learning hub does all of these). Browsers offer several storage options. This unit explains each, focusing on localStorage (the simplest and most used), with safe patterns, limits and security rules.

Why store data in the browser?

UseExample
PreferencesDark mode, language, font size
ConvenienceShopping cart before login, form drafts, recently viewed products
Offline supportSaved notes, cached data for apps that work offline (PWAs)
PerformanceAvoid reloading data that rarely changes

Data stored in the browser lives only on that device and browser. It's not shared with other devices, and users can clear it any time, so important data (orders, payments, accounts) must be saved on a server.

localStorage

localStorage.setItem("theme", "dark");          // save
const theme = localStorage.getItem("theme");    // read: "dark" (or null if missing)
localStorage.removeItem("theme");               // delete one key
localStorage.clear();                           // delete everything for this site
  • Keys and values are strings.
  • Each website (origin: protocol + domain + port) has its own separate storage.
  • Typical limit: about 5 MB per origin (varies by browser).
  • It's synchronous (blocks briefly); fine for small data.

sessionStorage

Same methods, but data lasts only for the current tab session and is cleared when the tab closes. Useful for temporary data like a multi-step form in progress.

localStoragesessionStorage
LifetimeUntil deletedUntil the tab closes
Shared between tabs of the same siteYesNo (each tab separate)
Typical usePreferences, cartTemporary wizard steps

Storing objects and arrays: use JSON

Storage only holds strings. Saving an object directly gives "[object Object]". Convert with JSON:

JavaScript · runs live in the interactive lesson
// A tiny in-memory stand-in for localStorage, so this runs anywhere
const storage = new Map();
const store = {
  setItem: (k, v) => storage.set(k, String(v)),
  getItem: (k) => (storage.has(k) ? storage.get(k) : null),
};

const cart = [{ id: 17, name: "Speaker", qty: 1 }, { id: 4, name: "Charger", qty: 2 }];

store.setItem("cart-wrong", cart);
console.log(store.getItem("cart-wrong"));            // "[object Object],[object Object]" (useless)

store.setItem("cart", JSON.stringify(cart));          // correct: save as JSON text
const saved = JSON.parse(store.getItem("cart"));      // read back as an array
console.log(saved.length, "items; first:", saved[0].name);

console.log(store.getItem("missing"));               // null when the key doesn't exist

Numbers and booleans come back as strings too: Number(localStorage.getItem("visits")), localStorage.getItem("muted") === "true".

Storage can fail: always use a safe wrapper

localStorage can throw errors:

  • In some private/incognito modes and in sandboxed frames (like this practice editor), access is blocked.
  • When storage is full (QuotaExceededError).
  • Saved text may be corrupted or from an older version of your app (invalid JSON).

A safe pattern:

JavaScript · runs live in the interactive lesson
const safeStore = {
  get(key, fallback) {
    try {
      const raw = globalThis.localStorage ? localStorage.getItem(key) : null;
      return raw === null ? fallback : JSON.parse(raw);
    } catch (e) {
      return fallback;                   // blocked or invalid JSON: use the default
    }
  },
  set(key, value) {
    try {
      localStorage.setItem(key, JSON.stringify(value));
      return true;
    } catch (e) {
      return false;                      // blocked or full: carry on without saving
    }
  },
};

console.log(safeStore.get("cart", []));                 // [] if nothing saved or storage blocked
console.log("Saved?", safeStore.set("theme", "dark"));  // false here (no localStorage in this runner)

Your app should work even if storage fails; it just won't remember things.

Example: a cart that survives reloads

HTML · runs live in the interactive lesson
<h3>Cart (<span id="count">0</span> items)</h3>
<button data-name="Unga 2kg">Add Unga</button>
<button data-name="Sugar 1kg">Add Sugar</button>
<button id="clear">Clear</button>
<ul id="items"></ul>
<p id="note" style="color:#64748b"></p>
<script>
  const KEY = "demo-cart";
  let memory = [];
  function load() {
    try { return JSON.parse(localStorage.getItem(KEY)) || []; }
    catch (e) { document.querySelector("#note").textContent = "(Storage is blocked in this practice editor, so the cart lives in memory only. On a real site it would survive a reload.)"; return memory; }
  }
  function save(cart) {
    try { localStorage.setItem(KEY, JSON.stringify(cart)); } catch (e) { memory = cart; }
  }
  function render() {
    const cart = load();
    document.querySelector("#count").textContent = cart.length;
    const ul = document.querySelector("#items");
    ul.replaceChildren(...cart.map((name) => { const li = document.createElement("li"); li.textContent = name; return li; }));
  }
  document.querySelectorAll("[data-name]").forEach((btn) =>
    btn.addEventListener("click", () => { const cart = load(); cart.push(btn.dataset.name); save(cart); render(); }));
  document.querySelector("#clear").addEventListener("click", () => { save([]); render(); });
  render();
</script>

On your own site (not in the sandbox), add items, reload the page, and the cart is still there.

Example: remembering dark mode

const saved = localStorage.getItem("theme");                // "dark", "light" or null
const prefersDark = matchMedia("(prefers-color-scheme: dark)").matches;
document.documentElement.dataset.theme = saved || (prefersDark ? "dark" : "light");

Run this in a small script in the <head> so the right theme appears before the page paints (no flash of the wrong theme).

Listening for changes in other tabs

When localStorage changes in another tab of the same site, a storage event fires, so you can keep tabs in sync (e.g. cart count):

window.addEventListener("storage", (e) => {
  if (e.key === "cart") updateCartBadge(JSON.parse(e.newValue || "[]"));
});

Cookies

Cookies are small pieces of data (about 4 KB each) that the browser sends to the server with every request to that site. They're mainly for sessions/logins and server-side features.

localStorageCookies
Sent to the server automaticallyNoYes, on every request
Size~5 MB~4 KB per cookie
ExpiryNone (until deleted)Set by the site
Readable by JavaScriptYesYes, unless HttpOnly
Best forClient-side preferences and cachesLogin sessions (set by the server)

Secure session cookies are set by the server with HttpOnly (JavaScript can't read them, protecting against theft), Secure (HTTPS only) and SameSite (protects against cross-site request forgery).

IndexedDB

For large or structured data (thousands of records, offline apps, files), browsers offer IndexedDB: an asynchronous database in the browser. Its API is more complex; libraries like idb or Dexie make it easier. Use it for offline-first apps (e.g. a field data collection app that syncs when back online).

Security and privacy rules

  • Store only what's necessary; it's personal data on a shared device in many households and cyber cafés.
  • Provide a way to clear data (e.g. "Sign out" clears cached personal data).
  • Kenya's Data Protection Act applies to personal data you collect, including what you store about users.
  • Respect users' choices about cookies and tracking (consent for analytics/ads cookies where required).
Think about it: A school portal stores each student's full name, results and login token in localStorage so pages load faster. What problems could this cause on a shared computer in a cyber café?Show answer

The next person using that browser could read the stored results and token (via DevTools or a malicious script) and possibly access the student's account. Sensitive data and tokens shouldn't be in localStorage; use server sessions with HttpOnly cookies, short session lifetimes, and clear any cached data on sign-out.

Common mistakes

MistakeFix
Saving objects without JSON.stringifyStringify on save, parse on load
Not handling null for missing keysProvide defaults
No try...catch around storageWrap in a safe helper
Storing secrets or tokensServer sessions + HttpOnly cookies
Relying on localStorage for important dataSave orders/accounts on the server
Huge data in localStorageIndexedDB for large data

Practice tasks

  1. Save a visitor's name from an input and greet them on the next visit (test on your own computer, not the sandbox).
  2. Count page visits with localStorage and display "You've visited X times".
  3. Save a form draft on every input event and restore it on load.
  4. Build a dark mode toggle that remembers the choice.
  5. Write a safeStore helper and use it in a small project.

Summary

  • Browser storage remembers data on one device: preferences, carts, drafts, offline data.
  • localStorage persists; sessionStorage lasts for the tab; both store strings via setItem, getItem, removeItem, clear.
  • Use JSON for objects/arrays; wrap storage in try...catch; handle null defaults.
  • Cookies travel to the server and suit sessions (HttpOnly, Secure, SameSite); IndexedDB suits large structured data.
  • Never store secrets or tokens in localStorage; keep important data on the server.

Check yourself

  1. Which storage keeps data after the browser is closed: localStorage or sessionStorage?

    Show answer

    localStorage

  2. Which method saves a value in localStorage?

    Show answer

    setItem

  3. What does getItem return when the key doesn't exist?

    Show answer

    null

  4. What must you do to an object before saving it in localStorage?

    Show answer

    JSON.stringify

  5. Roughly how much data can localStorage hold per site? Write in MB.

    Show answer

    5MB

  6. Which cookie flag stops JavaScript from reading a cookie?

    Show answer

    HttpOnly

  7. Should login tokens or passwords be stored in localStorage? (yes or no)

    Show answer

    no

Lesson 17 of 21 in JavaScript · Printable course notes