Modern web applications need to remember user preferences, save form data, cache information, and maintain state across sessions. The Web Storage API provides localStorage and sessionStorage—simple key-value stores built into browsers. localStorage persists data indefinitely, while sessionStorage only lasts for the browser session. Unlike cookies, storage APIs are easy to use, have generous size limits (5-10MB), and don't send data with every HTTP request. Let's master browser storage!
What is Local Storage?
Web Storage API Features:
- localStorage: Persists indefinitely (no expiration)
- sessionStorage: Clears when tab/window closes
- Stores up to 5-10MB (browser dependent)
- Simple key-value storage (strings only)
- Synchronous API (easy to use)
- Per origin (domain + protocol + port)
- Not sent with HTTP requests (unlike cookies)
- Accessible only to client-side JavaScript
localStorage is much simpler
Cookies
// Cookies (old way)
// Complex syntax
document.cookie = "username=Alice; expires=Fri, 31 Dec 2024 23:59:59 GMT; path=/";
// Difficult to read
function getCookie(name) {
let matches = document.cookie.match(
new RegExp("(?:^|; )" + name + "=([^;]*)")
);
return matches ? matches[1] : null;
}
// Limited size (4KB)
// Sent with every request
// Difficult to managelocalStorage
// localStorage (modern way)
// Simple syntax
localStorage.setItem('username', 'Alice');
// Easy to read
let username = localStorage.getItem('username');
// Generous size (5-10MB)
// Not sent with requests
// Easy to manage
// No expiration needed
// Data persists foreverBasic Operations
Storing Data (setItem)
// Store string value
localStorage.setItem('username', 'Alice');
localStorage.setItem('theme', 'dark');
localStorage.setItem('fontSize', '16');
// Alternative syntax (not recommended)
localStorage.username = 'Alice';
localStorage['theme'] = 'dark';
// Storing numbers (converted to strings automatically)
localStorage.setItem('count', 42);
localStorage.setItem('price', 99.99);
// Verify storage
console.log(localStorage.getItem('username')); // "Alice"
console.log(localStorage.getItem('count')); // "42" (string!)
console.log(typeof localStorage.getItem('count')); // "string"Retrieving Data (getItem)
// Get stored value
let username = localStorage.getItem('username');
console.log(username); // "Alice"
// Get non-existent key returns null
let missing = localStorage.getItem('nonexistent');
console.log(missing); // null
// Alternative syntax (not recommended)
let theme = localStorage.theme;
let font = localStorage['fontSize'];
// With default value
let username = localStorage.getItem('username') || 'Guest';
let theme = localStorage.getItem('theme') || 'light';
// Nullish coalescing (better)
let fontSize = localStorage.getItem('fontSize') ?? '14';
// Use ?? because '0' is a valid value
// Parse numbers
let count = parseInt(localStorage.getItem('count')) || 0;
let price = parseFloat(localStorage.getItem('price')) || 0;Removing Data (removeItem)
// Remove specific item
localStorage.removeItem('username');
// Verify removal
console.log(localStorage.getItem('username')); // null
// Remove all items
localStorage.clear();
// Verify clear
console.log(localStorage.length); // 0
// Alternative syntax (not recommended)
delete localStorage.username;
// Check if key exists
function hasKey(key) {
return localStorage.getItem(key) !== null;
}
console.log(hasKey('theme')); // true or falseChecking Storage
// Get number of items
console.log(localStorage.length); // Number of stored items
// Get key by index
let firstKey = localStorage.key(0);
console.log(firstKey);
// List all keys
function getAllKeys() {
let keys = [];
for (let i = 0; i < localStorage.length; i++) {
keys.push(localStorage.key(i));
}
return keys;
}
console.log(getAllKeys()); // ['username', 'theme', 'fontSize']
// List all items
function getAllItems() {
let items = {};
for (let i = 0; i < localStorage.length; i++) {
let key = localStorage.key(i);
items[key] = localStorage.getItem(key);
}
return items;
}
console.log(getAllItems());
// { username: 'Alice', theme: 'dark', fontSize: '16' }Storing Objects and Arrays
localStorage only stores strings. Use JSON.stringify() and JSON.parse() for objects/arrays.
// Store object
let user = {
name: 'Alice',
age: 25,
email: 'alice@example.com',
preferences: {
theme: 'dark',
notifications: true
}
};
// Convert to JSON string
localStorage.setItem('user', JSON.stringify(user));
// Retrieve and parse
let savedUser = JSON.parse(localStorage.getItem('user'));
console.log(savedUser.name); // "Alice"
console.log(savedUser.preferences.theme); // "dark"
// Store array
let todos = [
{ id: 1, text: 'Learn JavaScript', done: true },
{ id: 2, text: 'Build project', done: false },
{ id: 3, text: 'Deploy app', done: false }
];
localStorage.setItem('todos', JSON.stringify(todos));
// Retrieve and parse
let savedTodos = JSON.parse(localStorage.getItem('todos'));
console.log(savedTodos.length); // 3
console.log(savedTodos[0].text); // "Learn JavaScript"
// Safe retrieval with default
function getStoredObject(key, defaultValue = null) {
try {
let item = localStorage.getItem(key);
return item ? JSON.parse(item) : defaultValue;
} catch (error) {
console.error('Failed to parse stored data:', error);
return defaultValue;
}
}
// Usage
let user = getStoredObject('user', { name: 'Guest' });
let todos = getStoredObject('todos', []);sessionStorage
sessionStorage works exactly like localStorage, but data is cleared when the tab/window closes.
// Same API as localStorage
sessionStorage.setItem('tempData', 'This will be cleared');
sessionStorage.setItem('formData', JSON.stringify({ name: 'Alice' }));
let temp = sessionStorage.getItem('tempData');
console.log(temp); // "This will be cleared"
sessionStorage.removeItem('tempData');
sessionStorage.clear();
// Use cases for sessionStorage:
// - Temporary form data (multi-step forms)
// - Shopping cart for single session
// - Temporary API tokens
// - Page state during navigation
// - One-time notifications
// Use cases for localStorage:
// - User preferences (theme, language)
// - Authentication tokens (with caution!)
// - Cached data
// - User-created content (drafts)
// - Settings
// Example: Multi-step form with sessionStorage
function saveFormStep(stepNumber, data) {
sessionStorage.setItem(`formStep${stepNumber}`, JSON.stringify(data));
}
function getFormStep(stepNumber) {
let data = sessionStorage.getItem(`formStep${stepNumber}`);
return data ? JSON.parse(data) : null;
}
// Save form data
saveFormStep(1, { name: 'Alice', email: 'alice@test.com' });
saveFormStep(2, { address: '123 Main St', city: 'NYC' });
// Retrieve on next step
let step1 = getFormStep(1);
console.log(step1.name); // "Alice"Practical Patterns
Pattern 1: Storage Helper Class
// Reusable storage helper
class Storage {
constructor(storage = localStorage) {
this.storage = storage;
}
set(key, value) {
try {
let serialized = JSON.stringify(value);
this.storage.setItem(key, serialized);
return true;
} catch (error) {
console.error('Storage error:', error);
return false;
}
}
get(key, defaultValue = null) {
try {
let item = this.storage.getItem(key);
return item ? JSON.parse(item) : defaultValue;
} catch (error) {
console.error('Parse error:', error);
return defaultValue;
}
}
remove(key) {
this.storage.removeItem(key);
}
clear() {
this.storage.clear();
}
has(key) {
return this.storage.getItem(key) !== null;
}
keys() {
let keys = [];
for (let i = 0; i < this.storage.length; i++) {
keys.push(this.storage.key(i));
}
return keys;
}
size() {
return this.storage.length;
}
}
// Usage
let store = new Storage(localStorage);
store.set('user', { name: 'Alice', age: 25 });
let user = store.get('user');
console.log(user.name); // "Alice"
store.set('count', 42);
console.log(store.get('count')); // 42 (parsed as number)
console.log(store.has('user')); // true
console.log(store.keys()); // ['user', 'count']
store.remove('count');
console.log(store.size()); // 1Pattern 2: Namespaced Storage
// Avoid key conflicts with namespacing
class NamespacedStorage {
constructor(namespace, storage = localStorage) {
this.namespace = namespace;
this.storage = storage;
}
_key(key) {
return `${this.namespace}:${key}`;
}
set(key, value) {
try {
this.storage.setItem(this._key(key), JSON.stringify(value));
return true;
} catch (error) {
console.error('Storage error:', error);
return false;
}
}
get(key, defaultValue = null) {
try {
let item = this.storage.getItem(this._key(key));
return item ? JSON.parse(item) : defaultValue;
} catch (error) {
return defaultValue;
}
}
remove(key) {
this.storage.removeItem(this._key(key));
}
clear() {
// Only clear items in this namespace
let keys = this.keys();
keys.forEach(key => this.remove(key));
}
keys() {
let prefix = `${this.namespace}:`;
let keys = [];
for (let i = 0; i < this.storage.length; i++) {
let key = this.storage.key(i);
if (key && key.startsWith(prefix)) {
keys.push(key.substring(prefix.length));
}
}
return keys;
}
}
// Usage
let appStorage = new NamespacedStorage('myApp');
let userStorage = new NamespacedStorage('user');
appStorage.set('version', '1.0.0');
userStorage.set('name', 'Alice');
console.log(appStorage.get('version')); // "1.0.0"
console.log(userStorage.get('name')); // "Alice"
// Keys are namespaced in actual storage:
// "myApp:version" = "1.0.0"
// "user:name" = "Alice"
appStorage.clear(); // Only clears "myApp:*" keys
console.log(userStorage.get('name')); // Still exists!Pattern 3: Storage with Expiration
// Add expiration to localStorage items
class ExpiringStorage {
set(key, value, expiresInMs) {
let item = {
value: value,
expiry: Date.now() + expiresInMs
};
localStorage.setItem(key, JSON.stringify(item));
}
get(key, defaultValue = null) {
try {
let itemStr = localStorage.getItem(key);
if (!itemStr) {
return defaultValue;
}
let item = JSON.parse(itemStr);
// Check if expired
if (Date.now() > item.expiry) {
localStorage.removeItem(key);
return defaultValue;
}
return item.value;
} catch (error) {
return defaultValue;
}
}
remove(key) {
localStorage.removeItem(key);
}
}
// Usage
let store = new ExpiringStorage();
// Store for 1 hour (3600000 ms)
store.set('tempToken', 'abc123', 3600000);
// Store for 5 minutes
store.set('cacheData', { users: [] }, 5 * 60 * 1000);
// Retrieve (auto-expires)
let token = store.get('tempToken');
console.log(token); // "abc123" or null if expired
// Practical: Cache API responses
async function getCachedUsers() {
let store = new ExpiringStorage();
// Check cache first (5 minute expiry)
let cached = store.get('users');
if (cached) {
console.log('Using cached data');
return cached;
}
// Fetch from API
console.log('Fetching from API');
let response = await fetch('/api/users');
let users = await response.json();
// Cache for 5 minutes
store.set('users', users, 5 * 60 * 1000);
return users;
}Practical Examples
Example 1: User Preferences
// Persistent user preferences
class UserPreferences {
constructor() {
this.key = 'userPreferences';
this.defaults = {
theme: 'light',
language: 'en',
fontSize: 14,
notifications: true,
autoSave: true
};
}
load() {
try {
let prefs = localStorage.getItem(this.key);
return prefs ? { ...this.defaults, ...JSON.parse(prefs) } : this.defaults;
} catch (error) {
console.error('Failed to load preferences:', error);
return this.defaults;
}
}
save(preferences) {
try {
localStorage.setItem(this.key, JSON.stringify(preferences));
return true;
} catch (error) {
console.error('Failed to save preferences:', error);
return false;
}
}
get(key) {
let prefs = this.load();
return prefs[key];
}
set(key, value) {
let prefs = this.load();
prefs[key] = value;
this.save(prefs);
}
reset() {
this.save(this.defaults);
}
}
// Usage
let prefs = new UserPreferences();
// Load preferences
let currentPrefs = prefs.load();
console.log('Theme:', currentPrefs.theme);
// Update single preference
prefs.set('theme', 'dark');
prefs.set('fontSize', 16);
// Apply theme
document.body.className = prefs.get('theme');
document.body.style.fontSize = prefs.get('fontSize') + 'px';
// Reset to defaults
prefs.reset();Example 2: Shopping Cart
// Persistent shopping cart
class ShoppingCart {
constructor() {
this.key = 'shoppingCart';
this.items = this.load();
}
load() {
try {
let items = localStorage.getItem(this.key);
return items ? JSON.parse(items) : [];
} catch (error) {
console.error('Failed to load cart:', error);
return [];
}
}
save() {
try {
localStorage.setItem(this.key, JSON.stringify(this.items));
} catch (error) {
console.error('Failed to save cart:', error);
}
}
addItem(product, quantity = 1) {
let existing = this.items.find(item => item.id === product.id);
if (existing) {
existing.quantity += quantity;
} else {
this.items.push({
...product,
quantity
});
}
this.save();
}
removeItem(productId) {
this.items = this.items.filter(item => item.id !== productId);
this.save();
}
updateQuantity(productId, quantity) {
let item = this.items.find(item => item.id === productId);
if (item) {
item.quantity = quantity;
this.save();
}
}
clear() {
this.items = [];
this.save();
}
getTotal() {
return this.items.reduce((total, item) => {
return total + (item.price * item.quantity);
}, 0);
}
getCount() {
return this.items.reduce((count, item) => count + item.quantity, 0);
}
}
// Usage
let cart = new ShoppingCart();
// Add products
cart.addItem({ id: 1, name: 'Book', price: 20 }, 2);
cart.addItem({ id: 2, name: 'Pen', price: 2 }, 5);
console.log('Total:', cart.getTotal()); // 50
console.log('Items:', cart.getCount()); // 7
// Update quantity
cart.updateQuantity(1, 3);
// Remove item
cart.removeItem(2);
// Cart persists across page reloads!Example 3: Form Auto-Save
// Auto-save form data as user types
class FormAutoSave {
constructor(formId, storageKey) {
this.form = document.getElementById(formId);
this.storageKey = storageKey;
if (this.form) {
this.init();
}
}
init() {
// Load saved data
this.load();
// Save on input
this.form.addEventListener('input', () => {
this.save();
});
// Clear on submit
this.form.addEventListener('submit', () => {
this.clear();
});
}
save() {
let formData = new FormData(this.form);
let data = {};
for (let [key, value] of formData.entries()) {
data[key] = value;
}
sessionStorage.setItem(this.storageKey, JSON.stringify(data));
console.log('Form auto-saved');
}
load() {
try {
let saved = sessionStorage.getItem(this.storageKey);
if (!saved) return;
let data = JSON.parse(saved);
// Restore form values
Object.keys(data).forEach(key => {
let input = this.form.elements[key];
if (input) {
input.value = data[key];
}
});
console.log('Form data restored');
} catch (error) {
console.error('Failed to load form data:', error);
}
}
clear() {
sessionStorage.removeItem(this.storageKey);
}
}
// Usage
// <form id="contactForm">
// <input name="name" required>
// <input name="email" type="email" required>
// <textarea name="message" required></textarea>
// <button type="submit">Submit</button>
// </form>
let autoSave = new FormAutoSave('contactForm', 'contactFormData');
// Form data auto-saves and restores on refresh!Local Storage Mastery
Master browser storage
console.log() to see your output in the console above.Storage Limits and Considerations
Important Considerations:
- Size Limit: 5-10MB per origin (browser dependent)
- Strings Only: Must stringify objects/arrays
- Synchronous: Blocks JavaScript execution
- Same Origin: Cannot access across different domains
- Not Secure: Don't store sensitive data (passwords, tokens)
- User Can Delete: Users can clear storage anytime
- Private Browsing: May not persist in incognito mode
// Check if storage is available
function isStorageAvailable(type = 'localStorage') {
try {
let storage = window[type];
let test = '__storage_test__';
storage.setItem(test, test);
storage.removeItem(test);
return true;
} catch (error) {
return false;
}
}
if (isStorageAvailable('localStorage')) {
console.log('localStorage is available');
} else {
console.log('localStorage is not available (private browsing?)');
}
// Check remaining space (approximate)
function checkStorageSpace() {
let total = 0;
for (let key in localStorage) {
if (localStorage.hasOwnProperty(key)) {
total += localStorage[key].length + key.length;
}
}
return total;
}
let usedBytes = checkStorageSpace();
console.log(`Storage used: ${(usedBytes / 1024).toFixed(2)} KB`);
// Handle quota exceeded error
function safeSetItem(key, value) {
try {
localStorage.setItem(key, value);
return true;
} catch (error) {
if (error.name === 'QuotaExceededError') {
console.error('Storage quota exceeded');
// Handle: clear old data, notify user, etc.
return false;
}
throw error;
}
}Key Takeaways
- localStorage persists data indefinitely across sessions
- sessionStorage clears when tab/window closes
- Only stores strings—use JSON.stringify/parse for objects
setItem(key, value)stores datagetItem(key)retrieves data (returns null if missing)removeItem(key)deletes specific itemclear()removes all items- Storage limit: 5-10MB per origin
- Always use try-catch for safety
- Don't store sensitive data (not encrypted)
What's Next?
Congratulations! You've completed the Working with Data section and now understand browser storage! You've learned localStorage, sessionStorage, storing objects, and building practical storage systems.
You've now completed 56 out of 62 JavaScript tutorial pages (90% complete)! You've finished 13 major sections. You're in the final stretch—just 6 more pages to go!
💪 Practice Challenge:
Before moving on, try:
- Store and retrieve user preferences with localStorage
- Build a shopping cart that persists across sessions
- Create a storage helper class with error handling
- Implement form auto-save with sessionStorage
- Add expiration time to stored items