Learn to store and retrieve data using browser storage APIs for persistent applications and user preferences.
Modern web applications need to store data on the client-side to provide better user experiences, remember user preferences, and maintain application state across browser sessions. Browser storage APIs provide different ways to persist data locally, from temporary session data to permanent user settings. This lesson covers localStorage and sessionStorage APIs, JSON data handling, storage events, quotas and limitations, user preferences management, and best practices for secure and efficient data persistence.
Browser storage APIs allow web applications to store data locally on the user's device, providing persistence and better user experiences without requiring server communication for every interaction.
Types of Browser Storage:
localStorage - Persistent storage that survives browser restarts and remains available until explicitly cleared by the user or application. Perfect for user preferences, settings, and data that should persist across sessions.
sessionStorage - Temporary storage that lasts only for the browser session and is cleared when the tab is closed. Ideal for temporary data, form state, or session-specific information.
Key Differences:
1// localStorage - data persists across browser restarts
2localStorage.setItem('permanentSetting', 'persistent value');
3localStorage.setItem('userPreferences', 'saved preferences');
4
5// sessionStorage - data persists only for the current session
6sessionStorage.setItem('currentSession', 'abc123');
7sessionStorage.setItem('tempData', 'temporary value');
8
9console.log('Session data:', sessionStorage.getItem('currentSession'));
10console.log('Persistent data:', localStorage.getItem('permanentSetting'));Storage Availability Check:
1function storageAvailable(type) {
2 try {
3 const storage = window[type];
4 const test = '__storage_test__';
5 storage.setItem(test, test);
6 storage.removeItem(test);
7 return true;
8 } catch (e) {
9 return false;
10 }
11}
12
13console.log('localStorage available:', storageAvailable('localStorage'));
14console.log('sessionStorage available:', storageAvailable('sessionStorage'));The localStorage API provides simple methods for storing, retrieving, and managing data. All data is stored as strings, so complex data types need to be serialized.
Basic Local Storage Operations:
1// Storing data in localStorage
2localStorage.setItem('username', 'john_doe');
3localStorage.setItem('theme', 'dark');
4localStorage.setItem('userAge', '25');
5
6// Retrieving data from localStorage
7const username = localStorage.getItem('username');
8const theme = localStorage.getItem('theme');
9const age = localStorage.getItem('userAge');
10
11console.log('Username:', username); // "john_doe"
12console.log('Theme:', theme); // "dark"
13console.log('Age:', age); // "25" (always returns string)
14
15// Convert string to number when needed
16const ageNumber = parseInt(localStorage.getItem('userAge'), 10);
17console.log('Age as number:', ageNumber); // 25
18
19// Check if item exists
20if (localStorage.getItem('theme') !== null) {
21 console.log('Theme setting found');
22} else {
23 console.log('No theme setting found');
24}Storage Management Methods:
1// Remove specific item
2localStorage.removeItem('userAge');
3console.log('Age after removal:', localStorage.getItem('userAge')); // null
4
5// Get all localStorage keys and values
6console.log('Total items in localStorage:', localStorage.length);
7for (let i = 0; i < localStorage.length; i++) {
8 const key = localStorage.key(i);
9 const value = localStorage.getItem(key);
10 console.log(`${key}: ${value}`);
11}
12
13// Clear all localStorage data (use with caution!)
14// localStorage.clear();Safe Storage Wrapper:
1const SafeStorage = {
2 setItem: function(key, value) {
3 try {
4 if (storageAvailable('localStorage')) {
5 localStorage.setItem(key, value);
6 return true;
7 }
8 } catch (error) {
9 console.error('Storage error:', error);
10 }
11 return false;
12 },
13
14 getItem: function(key) {
15 try {
16 if (storageAvailable('localStorage')) {
17 return localStorage.getItem(key);
18 }
19 } catch (error) {
20 console.error('Storage error:', error);
21 }
22 return null;
23 }
24};Since browser storage only accepts strings, complex data structures like objects and arrays must be serialized using JSON methods.
JSON Serialization and Deserialization:
1// Storing objects and arrays requires JSON serialization
2const userProfile = {
3 id: 12345,
4 name: 'Alice Johnson',
5 email: '[email protected]',
6 preferences: {
7 theme: 'dark',
8 notifications: true,
9 language: 'en'
10 },
11 recentItems: ['item1', 'item2', 'item3']
12};
13
14// Store object as JSON string
15try {
16 const userProfileJSON = JSON.stringify(userProfile);
17 localStorage.setItem('userProfile', userProfileJSON);
18 console.log('User profile stored successfully');
19} catch (error) {
20 console.error('Error storing user profile:', error);
21}
22
23// Retrieve and parse JSON data
24try {
25 const storedProfileJSON = localStorage.getItem('userProfile');
26 if (storedProfileJSON) {
27 const storedProfile = JSON.parse(storedProfileJSON);
28 console.log('Retrieved user profile:', storedProfile);
29 console.log('User theme:', storedProfile.preferences.theme);
30 } else {
31 console.log('No user profile found');
32 }
33} catch (error) {
34 console.error('Error parsing user profile:', error);
35 // Handle corrupted data
36 localStorage.removeItem('userProfile');
37}JSON Storage Utility Functions:
1const JSONStorage = {
2 setObject: function(key, obj) {
3 try {
4 const jsonString = JSON.stringify(obj);
5 localStorage.setItem(key, jsonString);
6 return true;
7 } catch (error) {
8 console.error('Error storing object:', error);
9 return false;
10 }
11 },
12
13 getObject: function(key) {
14 try {
15 const jsonString = localStorage.getItem(key);
16 return jsonString ? JSON.parse(jsonString) : null;
17 } catch (error) {
18 console.error('Error parsing object:', error);
19 localStorage.removeItem(key); // Remove corrupted data
20 return null;
21 }
22 },
23
24 updateObject: function(key, updates) {
25 const existingObj = this.getObject(key) || {};
26 const updatedObj = { ...existingObj, ...updates };
27 return this.setObject(key, updatedObj);
28 }
29};
30
31// Example usage
32const settings = {
33 volume: 0.8,
34 quality: 'HD',
35 autoplay: false
36};
37
38JSONStorage.setObject('appSettings', settings);
39JSONStorage.updateObject('appSettings', { volume: 0.9, autoplay: true });
40
41const updatedSettings = JSONStorage.getObject('appSettings');
42console.log('Updated settings:', updatedSettings);Storage events allow different tabs and windows to communicate when localStorage data changes, enabling real-time synchronization across your application.
Listening for Storage Changes:
1// Listen for storage changes across tabs/windows
2window.addEventListener('storage', function(e) {
3 console.log('Storage change detected:');
4 console.log('Key:', e.key);
5 console.log('Old value:', e.oldValue);
6 console.log('New value:', e.newValue);
7 console.log('Storage area:', e.storageArea === localStorage ? 'localStorage' : 'sessionStorage');
8 console.log('URL:', e.url);
9
10 // Handle specific key changes
11 if (e.key === 'userTheme') {
12 updateTheme(e.newValue);
13 } else if (e.key === 'notifications') {
14 updateNotificationSettings(e.newValue);
15 }
16});
17
18function updateTheme(newTheme) {
19 if (newTheme) {
20 document.body.className = newTheme + '-theme';
21 console.log('Theme updated to:', newTheme);
22 }
23}
24
25function updateNotificationSettings(notificationSettings) {
26 try {
27 const settings = JSON.parse(notificationSettings);
28 console.log('Notification settings updated:', settings);
29 } catch (error) {
30 console.error('Invalid notification settings:', error);
31 }
32}Storage Manager for Cross-Tab Synchronization:
1class StorageManager {
2 constructor() {
3 this.listeners = new Map();
4 this.setupStorageListener();
5 }
6
7 setupStorageListener() {
8 window.addEventListener('storage', (e) => {
9 if (this.listeners.has(e.key)) {
10 const callback = this.listeners.get(e.key);
11 callback(e.newValue, e.oldValue);
12 }
13 });
14 }
15
16 // Set data with automatic cross-tab notification
17 setItem(key, value, notify = true) {
18 const oldValue = localStorage.getItem(key);
19 localStorage.setItem(key, value);
20
21 if (notify && this.listeners.has(key)) {
22 const callback = this.listeners.get(key);
23 callback(value, oldValue);
24 }
25 }
26
27 // Register callback for specific key changes
28 onChange(key, callback) {
29 this.listeners.set(key, callback);
30 }
31
32 // Remove listener
33 removeListener(key) {
34 this.listeners.delete(key);
35 }
36}
37
38// Usage example
39const storageManager = new StorageManager();
40
41// Listen for user preference changes
42storageManager.onChange('userPreferences', (newValue, oldValue) => {
43 console.log('User preferences changed');
44 try {
45 const preferences = JSON.parse(newValue);
46 applyUserPreferences(preferences);
47 } catch (error) {
48 console.error('Invalid preferences data:', error);
49 }
50});A comprehensive user preferences system demonstrates practical application of localStorage for maintaining user settings across sessions.
Complete User Preferences Management System:
1class UserPreferences {
2 constructor() {
3 this.key = 'userPreferences';
4 this.defaults = {
5 theme: 'light',
6 fontSize: '14px',
7 language: 'en',
8 notifications: {
9 email: true,
10 push: false,
11 sounds: true
12 },
13 layout: {
14 sidebar: 'left',
15 density: 'comfortable'
16 },
17 accessibility: {
18 highContrast: false,
19 reducedMotion: false
20 }
21 };
22
23 this.loadPreferences();
24 }
25
26 loadPreferences() {
27 try {
28 const stored = localStorage.getItem(this.key);
29 if (stored) {
30 this.preferences = { ...this.defaults, ...JSON.parse(stored) };
31 } else {
32 this.preferences = { ...this.defaults };
33 this.savePreferences();
34 }
35 } catch (error) {
36 console.error('Error loading preferences:', error);
37 this.preferences = { ...this.defaults };
38 localStorage.removeItem(this.key); // Remove corrupted data
39 }
40 }
41
42 savePreferences() {
43 try {
44 localStorage.setItem(this.key, JSON.stringify(this.preferences));
45 return true;
46 } catch (error) {
47 console.error('Error saving preferences:', error);
48 return false;
49 }
50 }
51
52 get(path) {
53 const keys = path.split('.');
54 let current = this.preferences;
55
56 for (const key of keys) {
57 if (current && typeof current === 'object' && key in current) {
58 current = current[key];
59 } else {
60 return undefined;
61 }
62 }
63
64 return current;
65 }
66
67 set(path, value) {
68 const keys = path.split('.');
69 const lastKey = keys.pop();
70 let current = this.preferences;
71
72 for (const key of keys) {
73 if (!(key in current)) {
74 current[key] = {};
75 }
76 current = current[key];
77 }
78
79 current[lastKey] = value;
80 return this.savePreferences();
81 }
82
83 reset() {
84 this.preferences = { ...this.defaults };
85 return this.savePreferences();
86 }
87}
88
89// Usage examples
90const userPrefs = new UserPreferences();
91
92// Get and set preferences
93console.log('Current theme:', userPrefs.get('theme'));
94console.log('Email notifications:', userPrefs.get('notifications.email'));
95
96userPrefs.set('theme', 'dark');
97userPrefs.set('notifications.push', true);Professional applications require robust storage utilities that handle errors gracefully, manage quotas, and provide convenient APIs.
Comprehensive Storage Utility:
1class StorageUtil {
2 constructor(storageType = 'localStorage') {
3 this.storage = window[storageType];
4 this.isAvailable = this.checkAvailability();
5 }
6
7 checkAvailability() {
8 try {
9 const test = '__storage_test__';
10 this.storage.setItem(test, test);
11 this.storage.removeItem(test);
12 return true;
13 } catch (error) {
14 console.warn('Storage not available:', error.message);
15 return false;
16 }
17 }
18
19 // Get with default value
20 get(key, defaultValue = null) {
21 if (!this.isAvailable) return defaultValue;
22
23 try {
24 const value = this.storage.getItem(key);
25 return value !== null ? value : defaultValue;
26 } catch (error) {
27 console.error('Error getting item:', error);
28 return defaultValue;
29 }
30 }
31
32 // Get parsed JSON with default
33 getJSON(key, defaultValue = null) {
34 if (!this.isAvailable) return defaultValue;
35
36 try {
37 const value = this.storage.getItem(key);
38 return value !== null ? JSON.parse(value) : defaultValue;
39 } catch (error) {
40 console.error('Error parsing JSON:', error);
41 this.remove(key); // Clean up corrupted data
42 return defaultValue;
43 }
44 }
45
46 // Set with automatic JSON stringification for objects
47 set(key, value) {
48 if (!this.isAvailable) return false;
49
50 try {
51 if (typeof value === 'object' && value !== null) {
52 value = JSON.stringify(value);
53 }
54 this.storage.setItem(key, value);
55 return true;
56 } catch (error) {
57 console.error('Error setting item:', error);
58 return false;
59 }
60 }
61
62 // Get all keys
63 keys() {
64 if (!this.isAvailable) return [];
65
66 try {
67 const keys = [];
68 for (let i = 0; i < this.storage.length; i++) {
69 const key = this.storage.key(i);
70 if (key) keys.push(key);
71 }
72 return keys;
73 } catch (error) {
74 console.error('Error getting keys:', error);
75 return [];
76 }
77 }
78}
79
80// Usage examples
81const localStorage = new StorageUtil('localStorage');
82const sessionStorage = new StorageUtil('sessionStorage');
83
84// Store different types of data
85localStorage.set('simpleString', 'Hello World');
86localStorage.set('numberValue', 42);
87localStorage.set('complexObject', {
88 user: 'Alice',
89 settings: { theme: 'dark', lang: 'en' },
90 scores: [100, 95, 87]
91});
92
93// Retrieve with defaults
94const userName = localStorage.get('user', 'Guest');
95const settings = localStorage.getJSON('settings', { theme: 'light' });
96console.log('User:', userName);
97console.log('Settings:', settings);Cache with Expiration:
1class ExpiringCache {
2 constructor(storage = new StorageUtil('localStorage')) {
3 this.storage = storage;
4 this.prefix = 'cache_';
5 }
6
7 set(key, value, expirationMinutes = 60) {
8 const cacheKey = this.prefix + key;
9 const expirationTime = Date.now() + (expirationMinutes * 60 * 1000);
10
11 const cacheData = {
12 value: value,
13 expiration: expirationTime
14 };
15
16 return this.storage.set(cacheKey, cacheData);
17 }
18
19 get(key, defaultValue = null) {
20 const cacheKey = this.prefix + key;
21 const cacheData = this.storage.getJSON(cacheKey);
22
23 if (!cacheData) return defaultValue;
24
25 if (Date.now() > cacheData.expiration) {
26 this.storage.remove(cacheKey);
27 return defaultValue;
28 }
29
30 return cacheData.value;
31 }
32}
33
34// Usage
35const cache = new ExpiringCache();
36
37// Cache API responses
38cache.set('userProfile', { name: 'Alice' }, 30); // 30 minutes
39cache.set('recentPosts', ['post1', 'post2'], 10); // 10 minutesYour progress is saved in this browser, on this device only.