Skip to main content

Browser Storage

Localstorage

localStorage stores data in the browser that persists even after the browser is closed.

Basic usage

// Save data
localStorage.setItem('username', 'Alice');
localStorage.setItem('theme', 'dark');

// Read data
const username = localStorage.getItem('username');
console.log(username); // "Alice"

// Remove data
localStorage.removeItem('username');

// Clear all
localStorage.clear();

Storing objects

localStorage only stores strings. Store objects as JSON:

const user = {
name: 'Alice',
email: 'alice@example.com',
preferences: { theme: 'dark' }
};

// Save (convert to JSON)
localStorage.setItem('user', JSON.stringify(user));

// Read (parse JSON)
const savedUser = JSON.parse(localStorage.getItem('user'));
console.log(savedUser.name); // "Alice"

Checking if key exists

const username = localStorage.getItem('username');
if (username) {
console.log('Username:', username);
} else {
console.log('No username stored');
}

// Or check if null
if (localStorage.getItem('username') !== null) {
// Key exists
}

Sessionstorage

sessionStorage works like localStorage, but data only lasts for the browser tab session (cleared when tab closes).

Usage

// Same API as localStorage
sessionStorage.setItem('tempData', 'value');
const data = sessionStorage.getItem('tempData');
sessionStorage.removeItem('tempData');

When to use sessionStorage

  • Temporary data that shouldn't persist
  • Data specific to one tab/session
  • Sensitive data (automatically cleared when tab closes)

When storage is appropriate

Good use cases

User preferences — theme, language, settings
Form drafts — save progress before submission
Shopping cart — persist cart between sessions
Authentication tokens — remember login (though be careful with security)
Application state — save UI state (scroll position, filters)

Not appropriate

Large data — localStorage has size limits (~5-10MB)
Sensitive data — passwords, credit cards (use secure server storage)
Temporary data — use variables or sessionStorage
Frequently changing data — can be slow

Persisting application state

Save and restore application state:

// Save state
function saveState() {
const state = {
todos: todos.items,
filter: todos.filter,
theme: currentTheme
};
localStorage.setItem('appState', JSON.stringify(state));
}

// Load state
function loadState() {
const saved = localStorage.getItem('appState');
if (saved) {
const state = JSON.parse(saved);
todos.items = state.todos || [];
todos.filter = state.filter || 'all';
currentTheme = state.theme || 'light';
renderTodos();
}
}

// Save on every change
function addTodo(text) {
todos.items.push({ id: Date.now(), text, completed: false });
renderTodos();
saveState(); // Persist to localStorage
}

// Load on page load
window.addEventListener('DOMContentLoaded', loadState);

Example: Save form drafts

const form = document.querySelector('#contact-form');

// Save form data as user types
form.addEventListener('input', function() {
const formData = new FormData(form);
const data = Object.fromEntries(formData);
localStorage.setItem('formDraft', JSON.stringify(data));
});

// Load saved draft
const draft = localStorage.getItem('formDraft');
if (draft) {
const data = JSON.parse(draft);
Object.keys(data).forEach(key => {
const input = form.querySelector(`[name="${key}"]`);
if (input) input.value = data[key];
});
}

// Clear draft on submit
form.addEventListener('submit', function() {
localStorage.removeItem('formDraft');
});

Storage events

Listen for storage changes (useful for syncing between tabs):

window.addEventListener('storage', function(e) {
console.log('Storage changed:', e.key);
console.log('Old value:', e.oldValue);
console.log('New value:', e.newValue);

// Update UI if key changed
if (e.key === 'theme') {
updateTheme(e.newValue);
}
});

Note: Storage events only fire in other tabs/windows, not the one that made the change.

Storage limitations

Size limits

  • localStorage/sessionStorage: ~5-10MB per origin (varies by browser)
  • Exceeding limit: throws QuotaExceededError

Handling quota errors

try {
localStorage.setItem('largeData', largeString);
} catch (e) {
if (e.name === 'QuotaExceededError') {
console.error('Storage quota exceeded');
// Handle: clear old data, compress, etc.
}
}

Privacy mode

Some browsers disable localStorage in private/incognito mode:

function isStorageAvailable() {
try {
localStorage.setItem('test', 'test');
localStorage.removeItem('test');
return true;
} catch (e) {
return false;
}
}

if (isStorageAvailable()) {
// Use localStorage
} else {
// Fallback to sessionStorage or memory
}

Best practices

1. Use try/catch

try {
localStorage.setItem('key', 'value');
} catch (e) {
console.error('Failed to save:', e);
}

2. Check before reading

const data = localStorage.getItem('key');
if (data) {
const parsed = JSON.parse(data);
// Use parsed data
}

3. Don't store sensitive data

// Bad: storing passwords
localStorage.setItem('password', password); // DON'T DO THIS!

// Good: store only what's necessary
localStorage.setItem('username', username);
// Password handled by server

4. Clean up old data

function cleanupOldData() {
const keys = Object.keys(localStorage);
keys.forEach(key => {
const data = localStorage.getItem(key);
// Check if data is old/stale
if (isStale(data)) {
localStorage.removeItem(key);
}
});
}

5. Use helper functions

const storage = {
set(key, value) {
try {
localStorage.setItem(key, JSON.stringify(value));
return true;
} catch (e) {
console.error('Storage error:', e);
return false;
}
},

get(key) {
try {
const item = localStorage.getItem(key);
return item ? JSON.parse(item) : null;
} catch (e) {
console.error('Storage error:', e);
return null;
}
},

remove(key) {
localStorage.removeItem(key);
},

clear() {
localStorage.clear();
}
};

// Usage
storage.set('user', { name: 'Alice' });
const user = storage.get('user');