JS LocalStorage API Guide: A Complete Tutorial
A complete tutorial on the JavaScript LocalStorage API. Covers getItem, setItem, removeItem, clear, key enumeration, storage limits, the storage event for cross-tab synchronization, JSON serialization patterns, error handling for quota exceeded, and building a persistent settings manager with localStorage.
LocalStorage provides a synchronous key-value store in the browser that persists across page reloads and browser restarts. It stores strings only, has a ~5MB limit per origin, and is accessible from any tab on the same origin. This guide covers every method, pattern, and pitfall.
LocalStorage Methods
| Method | Description | Returns |
|---|---|---|
setItem(key, value) | Store a value | undefined |
getItem(key) | Retrieve a value | string or null |
removeItem(key) | Delete a key | undefined |
clear() | Remove all keys for this origin | undefined |
key(index) | Get key name by index | string or null |
length | Number of stored keys | number |
Basic Operations
// Store a value (always a string)
localStorage.setItem("username", "parth");
// Retrieve a value
const username = localStorage.getItem("username"); // "parth"
// Non-existent keys return null
const missing = localStorage.getItem("nonexistent"); // null
// Remove a specific key
localStorage.removeItem("username");
// Clear everything for this origin
localStorage.clear();Storing Non-String Values
LocalStorage stores strings only. Numbers, booleans, objects, and arrays must be serialized:
// WRONG: stores "[object Object]"
localStorage.setItem("user", { name: "Parth" });
// CORRECT: serialize to JSON
localStorage.setItem("user", JSON.stringify({ name: "Parth", role: "developer" }));
// Read back and parse
const user = JSON.parse(localStorage.getItem("user"));
console.log(user.name); // "Parth"Safe Read With Fallback
function getStoredValue(key, fallback = null) {
try {
const raw = localStorage.getItem(key);
if (raw === null) return fallback;
return JSON.parse(raw);
} catch {
return fallback;
}
}
const theme = getStoredValue("theme", "light");
const prefs = getStoredValue("preferences", { fontSize: 16, language: "en" });See storing complex objects in JS localStorage guide for advanced serialization patterns.
Iterating All Keys
// Method 1: for loop with key()
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
const value = localStorage.getItem(key);
console.log(`${key}: ${value}`);
}
// Method 2: Object.keys
const allKeys = Object.keys(localStorage);
console.log(allKeys);
// Method 3: entries helper
function getAllEntries() {
const entries = {};
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
entries[key] = localStorage.getItem(key);
}
return entries;
}Storage Limits and Quota Handling
function safeSetItem(key, value) {
try {
localStorage.setItem(key, value);
return true;
} catch (error) {
if (error.name === "QuotaExceededError" || error.code === 22) {
console.error("LocalStorage quota exceeded");
// Optionally: evict old data
evictOldEntries();
try {
localStorage.setItem(key, value);
return true;
} catch {
return false;
}
}
return false;
}
}
function getStorageUsage() {
let total = 0;
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
const value = localStorage.getItem(key);
total += key.length + value.length;
}
return {
usedBytes: total * 2, // UTF-16 encoding
usedKB: Math.round((total * 2) / 1024),
estimatedLimitKB: 5120, // ~5MB typical
};
}Browser Storage Limits
| Browser | LocalStorage Limit | Per Origin |
|---|---|---|
| Chrome | 5 MB | Yes |
| Firefox | 5 MB | Yes |
| Safari | 5 MB | Yes |
| Edge | 5 MB | Yes |
| Mobile Safari | 5 MB | Yes |
Cross-Tab Synchronization With storage Event
// This fires in OTHER tabs when localStorage changes
window.addEventListener("storage", (event) => {
console.log("Storage changed in another tab:");
console.log("Key:", event.key);
console.log("Old value:", event.oldValue);
console.log("New value:", event.newValue);
console.log("URL that changed it:", event.url);
// React to specific changes
if (event.key === "theme") {
applyTheme(event.newValue);
}
if (event.key === "authToken" && event.newValue === null) {
// User logged out in another tab
redirectToLogin();
}
});The storage event only fires in tabs other than the one that made the change. It does not fire in the tab that called setItem.
Settings Manager
class SettingsManager {
constructor(prefix = "app") {
this.prefix = prefix;
this.defaults = {};
this.listeners = new Map();
}
setDefaults(defaults) {
this.defaults = { ...this.defaults, ...defaults };
}
get(key) {
const fullKey = `${this.prefix}:${key}`;
const raw = localStorage.getItem(fullKey);
if (raw === null) {
return this.defaults[key] ?? null;
}
try {
return JSON.parse(raw);
} catch {
return raw;
}
}
set(key, value) {
const fullKey = `${this.prefix}:${key}`;
const serialized = JSON.stringify(value);
try {
localStorage.setItem(fullKey, serialized);
this.notifyListeners(key, value);
return true;
} catch (error) {
console.error(`Failed to save setting "${key}":`, error.message);
return false;
}
}
remove(key) {
localStorage.removeItem(`${this.prefix}:${key}`);
this.notifyListeners(key, this.defaults[key] ?? null);
}
onChange(key, callback) {
if (!this.listeners.has(key)) {
this.listeners.set(key, []);
}
this.listeners.get(key).push(callback);
// Return unsubscribe function
return () => {
const cbs = this.listeners.get(key);
const idx = cbs.indexOf(callback);
if (idx > -1) cbs.splice(idx, 1);
};
}
notifyListeners(key, value) {
const cbs = this.listeners.get(key) || [];
cbs.forEach((cb) => cb(value));
}
getAll() {
const entries = {};
for (let i = 0; i < localStorage.length; i++) {
const fullKey = localStorage.key(i);
if (fullKey.startsWith(this.prefix + ":")) {
const key = fullKey.slice(this.prefix.length + 1);
entries[key] = this.get(key);
}
}
return { ...this.defaults, ...entries };
}
clearAll() {
const keys = [];
for (let i = 0; i < localStorage.length; i++) {
const fullKey = localStorage.key(i);
if (fullKey.startsWith(this.prefix + ":")) {
keys.push(fullKey);
}
}
keys.forEach((k) => localStorage.removeItem(k));
}
}
// Usage
const settings = new SettingsManager("runehub");
settings.setDefaults({ theme: "light", fontSize: 16, language: "en" });
settings.set("theme", "dark");
console.log(settings.get("theme")); // "dark"
console.log(settings.get("fontSize")); // 16 (default)
const unsub = settings.onChange("theme", (newTheme) => {
document.documentElement.dataset.theme = newTheme;
});Checking for LocalStorage Availability
function isLocalStorageAvailable() {
try {
const testKey = "__storage_test__";
localStorage.setItem(testKey, "1");
localStorage.removeItem(testKey);
return true;
} catch {
return false;
}
}
if (!isLocalStorageAvailable()) {
console.warn("LocalStorage is not available. Using in-memory fallback.");
}Rune AI
Key Insights
- Strings only: Always
JSON.stringifybefore storing andJSON.parseafter reading; direct object storage produces"[object Object]" - ~5MB per origin: Monitor usage with a size calculator and evict old entries when approaching the quota
- storage event for cross-tab sync: Fires in other tabs when localStorage changes, enabling theme sync, auth state propagation, and cart updates
- Not secure for sensitive data: Any JavaScript on the same origin can read localStorage; use httpOnly cookies for auth tokens
- Always wrap in try/catch: Safari private mode and quota limits can throw errors; provide fallbacks for unavailable storage
Frequently Asked Questions
What is the difference between localStorage and sessionStorage?
Is localStorage secure for storing tokens?
Does localStorage work in incognito/private mode?
Can localStorage trigger the storage event in the same tab?
How do I handle cookies vs localStorage for user preferences?
Conclusion
LocalStorage provides persistent, synchronous key-value storage with a ~5MB limit per origin. Always serialize objects with JSON.stringify, handle QuotaExceededError gracefully, use prefixed keys to avoid collisions, and leverage the storage event for cross-tab synchronization. For storing complex nested objects, see storing complex objects in JS localStorage guide. For the session-scoped alternative, see JS sessionStorage API guide complete tutorial.
More in this topic
OffscreenCanvas API in JS for UI Performance
Master the OffscreenCanvas API to offload rendering from the main thread. Covers worker-based 2D and WebGL rendering, animation loops inside workers, bitmap transfer, double buffering, chart rendering pipelines, image processing, and performance measurement strategies.
Advanced Web Workers for High Performance JS
Master Web Workers for truly parallel JavaScript execution. Covers dedicated and shared workers, structured cloning, transferable objects, SharedArrayBuffer with Atomics, worker pools, task scheduling, Comlink RPC patterns, module workers, and performance profiling strategies.
JavaScript Macros and Abstract Code Generation
Master JavaScript code generation techniques for compile-time and runtime metaprogramming. Covers AST manipulation, Babel plugin authorship, tagged template literals as macros, code generation pipelines, source-to-source transformation, compile-time evaluation, and safe eval alternatives.