Web Storage gives each origin two string maps that never travel to the server. localStorage survives restarts; sessionStorage belongs to one tab, survives reloads and is cleared when the tab closes. Both offer setItem(), getItem() (null if missing), removeItem(), clear(), key(i) and length, are Baseline Widely available, and hold 5 MiB each before setItem() throws QuotaExceededError.
localStorage.setItem('prefs', JSON.stringify({ theme: 'dark', fontSize: 18 }));
sessionStorage.setItem('draft', 'Dear Ann,'); // gone when this tab closes
localStorage.visits = 5; // stored as the string "5"
console.log(localStorage.visits + 1, JSON.parse(localStorage.prefs).fontSize + 2);
try {
localStorage.setItem('big', 'x'.repeat(6 * 1024 ** 2)); // over the 5 MiB limit
} catch (err) {
console.log(err.name);
}Output
51 20 QuotaExceededError
Other same-origin tabs get a storage event (key, oldValue, newValue) when localStorage changes, handy for syncing a logout. The API is synchronous and absent from workers: keep it for small settings, never tokens, and put large data in IndexedDB.