CodeMIRAI
0 XP

Saving data: localStorage and sessionStorage

5 min read 6 runnable examples Quiz included

Web pages forget everything when they're closed or reloaded, unless they save it somewhere. The Web Storage API gives every website two small key-value stores inside the browser: localStorage, which keeps data until it's deleted (even after the browser restarts), and sessionStorage, which keeps it only while the tab is open. They're how sites remember your dark-mode choice, a half-written comment, or a shopping cart without an account. CodeMIRAI keeps your progress in localStorage this way.

The basics

Both stores have the same methods, and both store strings only, under string keys:

MethodDoes
setItem(key, value)saves a value (turned into a string)
getItem(key)reads it, or null if there's nothing under that key
removeItem(key)deletes one key
clear()deletes everything this site stored
length / key(i)how many keys / the name of the i-th key
HTML
<script>
  localStorage.setItem("username", "Asha");
  localStorage.setItem("visits", 3);                 // stored as the string "3"

  console.log(localStorage.getItem("username"));
  console.log(typeof localStorage.getItem("visits")); // string!
  console.log(localStorage.getItem("missing"));       // null when nothing is saved

  localStorage.removeItem("username");
  console.log(localStorage.length, "key left:", localStorage.key(0));
</script>

Everything comes back as a string, so convert numbers with Number(), and remember that a missing key gives null.

Storing objects and arrays with JSON

To save an object or array, turn it into JSON text with JSON.stringify, and turn it back with JSON.parse:

HTML
<script>
  const settings = { theme: "dark", fontSize: 16, languages: ["js", "py"] };
  localStorage.setItem("settings", JSON.stringify(settings));

  const saved = JSON.parse(localStorage.getItem("settings"));
  console.log(saved.theme, saved.languages.length);

  localStorage.setItem("broken", { theme: "dark" });      // without stringify...
  console.log(localStorage.getItem("broken"));             // ...it's saved as "[object Object]"
</script>

Reading safely

Saved data can be missing (first visit), out of date, or damaged. A small helper that falls back to a default keeps the rest of your code simple:

HTML
<script>
  function load(key, fallback) {
    try {
      const text = localStorage.getItem(key);
      return text === null ? fallback : JSON.parse(text);
    } catch {
      return fallback;              // broken JSON, or storage blocked
    }
  }
  function save(key, value) {
    try {
      localStorage.setItem(key, JSON.stringify(value));
    } catch (err) {
      console.log("couldn't save:", err.name);   // full, or blocked in private mode
    }
  }

  localStorage.setItem("cart", "{oops");              // pretend it got damaged
  console.log(load("cart", []), load("prefs", { theme: "light" }));
  save("cart", [{ item: "pen", qty: 2 }]);
  console.log(load("cart", []));
</script>

getItem and setItem can also throw: when storage is full, or when the browser blocks it (some private modes, or a site's privacy settings). Wrap them in try/catch in real projects.

Example: remembering a theme

HTML
<style>
  body { font-family: system-ui; padding: 12px; transition: background .2s; }
  body.dark { background: #111827; color: #e5e7eb; }
</style>
<button id="toggle">Toggle dark mode</button>
<p>Reload the page on a real site and the choice stays.</p>
<script>
  const KEY = "theme";
  if (localStorage.getItem(KEY) === "dark") document.body.classList.add("dark");   // apply the saved choice
  document.querySelector("#toggle").addEventListener("click", () => {
    const dark = document.body.classList.toggle("dark");
    localStorage.setItem(KEY, dark ? "dark" : "light");
    console.log("saved theme:", localStorage.getItem(KEY));
  });
</script>

Example: a visit counter and a saved draft

HTML
<p id="visits"></p>
<textarea id="draft" rows="3" cols="40" placeholder="Start typing: it's saved as you go"></textarea>
<p id="saved" style="font: 12px system-ui; color: gray"></p>
<script>
  const visits = Number(localStorage.getItem("visits") ?? 0) + 1;
  localStorage.setItem("visits", visits);
  document.querySelector("#visits").textContent = `You've opened this page ${visits} time(s).`;

  const draft = document.querySelector("#draft");
  draft.value = localStorage.getItem("draft") ?? "";
  draft.addEventListener("input", () => {
    localStorage.setItem("draft", draft.value);
    document.querySelector("#saved").textContent = "Draft saved " + new Date().toLocaleTimeString();
  });
</script>

Example: a to-do list that's remembered

The usual pattern: load the data once at the start, change the data, then save and redraw after every change:

HTML
<form id="f"><input id="t" placeholder="New task" required> <button>Add</button></form>
<ul id="list"></ul>
<script>
  const KEY = "todos";
  let todos = JSON.parse(localStorage.getItem(KEY) ?? '["Read about localStorage"]');

  function commit() {
    localStorage.setItem(KEY, JSON.stringify(todos));
    const ul = document.querySelector("#list");
    ul.replaceChildren(...todos.map((text, i) => {
      const li = document.createElement("li");
      li.textContent = text + " ";
      const x = document.createElement("button");
      x.textContent = "done";
      x.addEventListener("click", () => { todos.splice(i, 1); commit(); });
      li.append(x);
      return li;
    }));
  }

  document.querySelector("#f").addEventListener("submit", (e) => {
    e.preventDefault();
    todos.push(document.querySelector("#t").value.trim());
    document.querySelector("#t").value = "";
    commit();
  });
  commit();
  console.log("stored:", localStorage.getItem(KEY));
</script>

localStorage or sessionStorage?

localStoragesessionStorage
Lastsuntil deleted (survives restarts)until the tab is closed
Shared betweenall tabs of the same siteonly this tab
Good forpreferences, saved progress, draftsa multi-step form in one tab, one-time messages

Both are tied to the origin (protocol + domain + port): https://codemirai.com can't read another site's storage, and http:// and https:// versions of a site are separate.

The storage event: other tabs

When one tab changes localStorage, other tabs of the same site get a storage event, which lets them stay in sync (for example, logging out everywhere at once):

JavaScript
window.addEventListener("storage", (e) => {
  console.log(`${e.key} changed from ${e.oldValue} to ${e.newValue}`);
  if (e.key === "theme") document.body.classList.toggle("dark", e.newValue === "dark");
});

The tab that made the change doesn't receive the event, only the others.

Limits and what not to store

  • About 5 MB per site. Fine for settings and small data; use IndexedDB for more.
  • Synchronous. Reads and writes pause the page briefly, so don't save huge values on every keystroke.
  • Not secure. Any JavaScript running on the page can read it, including an attacker's script if the site has an XSS hole. Never store passwords, card numbers or secret tokens in it.
  • Per browser, per device. Data saved on a laptop isn't on the phone. Syncing needs a server and an account (that's what CodeMIRAI's sign-in adds).
  • Users can clear it at any time, so treat it as a convenience, not the only copy of important data.

Other ways to keep data

SizeSent to the serverUse for
localStorage / sessionStorageabout 5 MBnosettings, drafts, small app data
cookiesabout 4 KBwith every requestsign-in sessions (set by the server, ideally HttpOnly)
IndexedDBlarge (hundreds of MB)nobig or structured data, offline apps, files
a server databaseunlimited(it is the server)anything shared between devices or people

Key points

  • localStorage keeps data across visits; sessionStorage only for the tab's life.
  • Both store strings: setItem, getItem (gives null if missing), removeItem, clear.
  • Store objects with JSON.stringify and read them back with JSON.parse, inside try/catch.
  • Load once at start, save after every change.
  • Storage is per site, per browser, about 5 MB, readable by any script on the page: no secrets.

Practice

  1. Save the user's name from an input, and greet them by name the next time the page loads.
  2. Make a counter with + and - buttons whose value is saved in localStorage.
  3. Save an array of favourite colours, add one, and read the array back.
  4. Write clearIfOlderThan(key, days) that stores a value with a saved date and deletes it once it's too old.
Finished this lesson?Mark it complete to earn +20 XP and move your progress on.