JS Sketchbooksee JavaScript think ✏️
← back to phase 7

7.7 — Storage & timing

Every mutation you've made (7.3) forgets everything the instant the page reloads — except when it doesn't. Today: the browser's built-in memory that survives a refresh (or deliberately doesn't), and the two moments a page actually finishes loading.

Both halves of this lesson matter for the exact same reason: they're about state and timing that a script — yours, or a testing tool's — needs to get right, or it quietly breaks.

watch it happen
localStorage.setItem("theme", "dark");
console.log(localStorage.getItem("theme"));

localStorage.setItem("user", JSON.stringify({ name: "Vasavi" }));
const user = JSON.parse(localStorage.getItem("user"));
console.log(user.name);

sessionStorage.setItem("draft", "hello");

document.addEventListener("DOMContentLoaded", () => {
  console.log("DOM ready");
});

window.addEventListener("load", () => {
  console.log("everything loaded");
});

localStorage.setItem(key, value) writes; localStorage.getItem(key) reads. A plain key-value store, always dealing in strings.

localStoragetheme: "dark"sessionStoragepage-load timelineDOMContentLoadedloadlocalStorage.setItem / getItem — a key-value STRING storeconsoledark
under the hood

The deeper story, with the real names for things — this part is what turns “I saw it” into “I can explain it.”

Storage limits and precision: localStorage/sessionStorage are typically capped around 5–10MB per origin (fine for settings and drafts, not for real datasets).

And every read/write is SYNCHRONOUS — it blocks the thread briefly, unlike the async IndexedDB you might meet for larger data.

Cookies carry two protections storage doesn't: HttpOnly (invisible to JavaScript entirely — a security measure against script-based theft) and Secure (sent only over HTTPS).

That's exactly why your test's own JS often can't read a login cookie directly — Playwright manages cookies through its own API instead, sidestepping the restriction properly.

Job note: browserContext.storageState() captures localStorage and cookies to a file in one call; loading that file before a test skips a slow login flow entirely — one of the biggest, cheapest speed wins in a real test suite, built entirely on today's two topics.

your turn

⌨️ build the localStorage shape

The sandbox has no real localStorage — so build a tiny working model of it: the exact three-verb shape the real API has.

requirements:

  • An object store, starting empty ({}) — the raw key-value data.
  • Three functions: setItem(key, value) (writes String(value) into store), getItem(key) (returns the value, or null if the key is missing), removeItem(key) (deletes the key).
  • setItem("theme", "dark"), print getItem("theme"). Then removeItem("theme"), print getItem("theme") again — must now be null.

when you press RUN, the console must show exactly:

dark
null

✏️ Quick check 1

localStorage.setItem("count", 5) stores a number. Type the TYPE that getItem("count") returns:

✏️ Quick check 2

Does sessionStorage survive closing and reopening the browser tab? Type yes or no.

✏️ Quick check 3

Which fires FIRST — DOMContentLoaded or load? Type it.

teach it back

🗣️ Now teach it back

Explain to a friend the difference between localStorage, sessionStorage, and cookies — and why DOMContentLoaded and load are two separate events instead of one.

Write it as if your friend is sitting next to you. Saved to your journal — future-you will use these notes to teach others.

a few sentences, minimum — you’ve got this
to remember
localStorage persists across restarts; sessionStorage dies with its tab. Both are string-only, origin-scoped. Cookies are the odd one out — sent to the SERVER with every request.
Everything you store is a string: JSON.stringify going in, JSON.parse coming out — 4.13’s wire-format discipline, reused.
DOMContentLoaded = tree ready; load = everything (images, styles, iframes) actually finished. The gap between them is a real source of "clicked too early" test flakiness.