Frontend Engineering/
Lesson

Every web app eventually needs to remember something, a user's theme preference, a shopping cart, a cached APIWhat is api?A set of rules that lets one program talk to another, usually over the internet, by sending requests and getting responses. response, or an authenticationWhat is authentication?Verifying who a user is, typically through credentials like a password or token. sessionWhat is session?A server-side record that tracks a logged-in user. The browser holds only a session ID in a cookie, and the server looks up the full data on each request.. The browser gives you four distinct tools for this, and choosing the wrong one creates real problems. Too small and your app crashes. Too persistent and you leak sensitive data. Too slow and your UI freezes.

Think of it like storage in a kitchen: you have a sticky note (cookies), a small drawer (localStorage/sessionStorage), and a full filing cabinet (IndexedDBWhat is indexeddb?A database built into the browser that stores structured data, supporting large datasets and offline-first apps.). Each has a purpose.

The four options at a glance

Quick comparison

FeaturelocalStoragesessionStorageIndexedDBCookies
Capacity~5-10MB~5-10MB~50MB+~4KB
Persists after closeYesNoYesYes (if expiry set)
AsyncNoNoYesNo
Data typesStrings onlyStrings onlyObjects, blobs, filesStrings only
Sent with HTTP requestsNoNoNoYes
JavaScript accessibleYesYesYesYes (unless HttpOnly)

Choosing the right tool

You do not need to memorize every detail right away. A simple mental checklist works well: does the server need this data? If yes, use a cookieWhat is cookie?A small piece of data the browser stores and automatically sends with every request to the matching server, often used for sessions.. Is it large or complex? Use IndexedDBWhat is indexeddb?A database built into the browser that stores structured data, supporting large datasets and offline-first apps.. Otherwise, localStorage or sessionStorage will handle it.

// Does the server need it? → Cookie
// e.g. auth session managed by server

// Is it large (>5MB) or structured? → IndexedDB
// e.g. cached product catalog, user-generated files

// Should it survive tab close? → localStorage
// e.g. user theme, language preference

// Should it disappear when tab closes? → sessionStorage
// e.g. multi-step form data, temporary UI state
02

localStorage and sessionStorage

These two share an identical APIWhat is api?A set of rules that lets one program talk to another, usually over the internet, by sending requests and getting responses.. The only difference is lifetime: localStorage survives indefinitely, sessionStorage is wiped when the tab closes. You can switch between them by just swapping the object name.

// Save a user preference persistently
localStorage.setItem('theme', 'dark');
const theme = localStorage.getItem('theme') || 'light';
document.body.className = theme;

// Save multi-step form data for current session only
sessionStorage.setItem('step1', JSON.stringify({ name: 'Alice', email: 'alice@example.com' }));
const step1 = JSON.parse(sessionStorage.getItem('step1'));
Both are synchronous. Reading and writing blocks the main thread. This is fine for small values, but do not use them in a loop over thousands of records, you will freeze the UI.
03

IndexedDBWhat is indexeddb?A database built into the browser that stores structured data, supporting large datasets and offline-first apps.

IndexedDB is the browser's built-in database. It stores JavaScript objects natively, supports indexes for fast lookups, and runs asynchronously so it never blocks rendering. The native APIWhat is api?A set of rules that lets one program talk to another, usually over the internet, by sending requests and getting responses. is event-based and verbose, so almost everyone uses the idb wrapper library.

import { openDB } from 'idb';

const db = await openDB('my-app', 1, {
  upgrade(db) {
    db.createObjectStore('tasks', { keyPath: 'id', autoIncrement: true });
  }
});

await db.add('tasks', { title: 'Learn IndexedDB', done: false });
const tasks = await db.getAll('tasks');

IndexedDB is ideal for offline-first apps, caching large datasets, or storing user-generated content like drafts and uploads.

04

Cookies

Cookies are the oldest mechanism and the only one where data flows automatically between browser and server. Every request to the matching domain includes the cookieWhat is cookie?A small piece of data the browser stores and automatically sends with every request to the matching server, often used for sessions. header, which is exactly what makes them useful for authenticationWhat is authentication?Verifying who a user is, typically through credentials like a password or token. sessions, and risky if misconfigured.

// Writing a cookie from JavaScript (client-side only, limited use)
document.cookie = 'user=alice; path=/; max-age=86400; samesite=strict';

// Reading all cookies (returns a single string, not an object)
console.log(document.cookie); // 'user=alice; session=xyz'
Sensitive cookies like session tokens should always be set by the server with HttpOnly and Secure flags. Client-side JavaScript should never touch your auth cookie.
05

Quick reference

Use caseRecommended storage
Theme / language preferencelocalStorage
Multi-step form statesessionStorage
Authentication session tokenHttpOnly cookie (server-set)
Large offline datasetIndexedDB
Shopping cart (temporary)sessionStorage or localStorage
Cached API responseIndexedDB
Small server-readable flagCookie
javascript
// Practical comparison of all 4 storage types

// ========== localStorage ==========
localStorage.setItem('username', 'Marie');
localStorage.setItem('settings', JSON.stringify({
  theme: 'dark',
  fontSize: 16,
  notifications: true
}));

const username = localStorage.getItem('username');
const settings = JSON.parse(localStorage.getItem('settings'));
localStorage.removeItem('username');

// ========== sessionStorage ==========
sessionStorage.setItem('currentStep', '2');
sessionStorage.setItem('formData', JSON.stringify({
  name: 'John',
  email: 'john@example.com'
}));

const currentStep = sessionStorage.getItem('currentStep');

// ========== Cookies ==========
document.cookie = 'user=John; expires=Fri, 31 Dec 2025 23:59:59 GMT; path=/; secure; samesite=strict';
console.log(document.cookie);
document.cookie = 'user=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/';

// ========== Utility wrapper ==========
const Store = {
  set(key, value) {
    localStorage.setItem(key, JSON.stringify(value));
  },
  get(key, defaultValue = null) {
    const item = localStorage.getItem(key);
    return item ? JSON.parse(item) : defaultValue;
  },
  remove(key) {
    localStorage.removeItem(key);
  },
  session: {
    set(key, value) {
      sessionStorage.setItem(key, JSON.stringify(value));
    },
    get(key, defaultValue = null) {
      const item = sessionStorage.getItem(key);
      return item ? JSON.parse(item) : defaultValue;
    }
  }
};

Store.set('user', { name: 'Marie', role: 'admin' });
console.log(Store.get('user')); // { name: 'Marie', role: 'admin' }