[JavaScript Series 9] Preserve State with localStorage and sessionStorage

한국어 버전

Imagine filling out a long form, refreshing the page by accident, and losing everything. The Web Storage API is a built-in key-value store for strings, and it helps preserve that progress. We will cover localStorage and sessionStorage in the order of concept → code → reason.

Key terms

  1. Web Storage API: a simple browser storage system that keeps key-value strings.
  2. localStorage: persistent storage that survives revisits to the same origin.
  3. sessionStorage: tab-scoped storage that disappears when the tab closes.
  4. JSON.stringify / JSON.parse: functions for serializing and restoring objects or arrays.
  5. TTL (Time To Live): storing an expiration timestamp alongside a value so you can expire it later.

Core ideas

  • localStorage: scoped to the origin and sticks around until deleted by the user or by code.
  • sessionStorage: scoped to the current tab, great for drafts or temporary filter values.
  • JSON serialization: storage only holds strings, so wrap data with JSON.stringify and unwrap with JSON.parse.
  • storage event: fires in other tabs of the same origin whenever localStorage changes, allowing real-time sync.
  • TTL: because storage has no notion of expiry, store { value, expiresAt } yourself.

Code examples

const STORAGE_KEY = "starter-dashboard";

function loadState(defaultValue) {
  try {
    const raw = localStorage.getItem(STORAGE_KEY);
    if (!raw) return defaultValue;
    return JSON.parse(raw);
  } catch (error) {
    console.error("Failed to parse storage", error);
    return defaultValue;
  }
}

function saveState(state) {
  localStorage.setItem(STORAGE_KEY, JSON.stringify(state));
}
const initial = loadState({ todos: [], theme: "light" });
const state = { ...initial };

function update(partial) {
  Object.assign(state, partial);
  render();
  saveState(state);
}

window.addEventListener("storage", (event) => {
  if (event.key !== STORAGE_KEY || !event.newValue) return;
  const next = JSON.parse(event.newValue);
  Object.assign(state, next);
  render();
});
const formDraftKey = "signup-draft";

function saveDraft(event) {
  const formData = new FormData(event.currentTarget);
  const draft = Object.fromEntries(formData.entries());
  sessionStorage.setItem(formDraftKey, JSON.stringify(draft));
}

function restoreDraft(form) {
  const raw = sessionStorage.getItem(formDraftKey);
  if (!raw) return;
  const draft = JSON.parse(raw);
  Object.entries(draft).forEach(([name, value]) => {
    const input = form.elements.namedItem(name);
    if (input instanceof HTMLInputElement) input.value = value;
  });
}

const form = document.querySelector(".signup-form");
form?.addEventListener("input", debounce(saveDraft, 400));
if (form) restoreDraft(form);
function setWithExpiration(key, value, ttlMs) {
  const payload = { value, expiresAt: Date.now() + ttlMs };
  localStorage.setItem(key, JSON.stringify(payload));
}

function getWithExpiration(key) {
  const raw = localStorage.getItem(key);
  if (!raw) return null;
  const payload = JSON.parse(raw);
  if (Date.now() > payload.expiresAt) {
    localStorage.removeItem(key);
    return null;
  }
  return payload.value;
}

Why it matters

  • localStorage keeps prototypes trustworthy by surviving refreshes and restarts.
  • sessionStorage drafts restore long forms when someone navigates away briefly.
  • The storage event and TTL pattern prevent UIs from drifting apart across tabs.

Practice

  • Follow along: connect loadState / saveState to your todo data and confirm it survives refreshes.
  • Extend: keep form drafts in sessionStorage, and in a second tab sync the list via the storage event.
  • Debug: trigger JSON parse errors or quota issues on purpose, log them, and fall back to safe defaults.
  • Done when: state saves automatically without a dedicated button and cross-tab edits trigger immediate re-rendering.

Wrap-up

Web storage acts like a “tiny server” inside the browser. Next we will use the persisted data alongside the event loop and debounce/throttle controls to boost responsiveness.

💬 댓글

이 글에 대한 의견을 남겨주세요