Master client-side data storage with localStorage, sessionStorage, IndexedDB, and cookies. Learn data persistence strategies for modern web applications.
Browser storage APIs allow web applications to store data locally on the user's device, enabling offline functionality, performance optimization, and enhanced user experiences. Each storage method has specific capabilities and use cases.
Web Storage APIs provide simple key-value storage that's easy to use and widely supported.
localStorage - Persistent Storage:
Data persists until explicitly cleared
1// Store data
2localStorage.setItem('username', 'john_doe');
3localStorage.setItem('settings', JSON.stringify({
4 theme: 'dark',
5 notifications: true
6}));
7
8// Retrieve data
9const username = localStorage.getItem('username');
10const settings = JSON.parse(localStorage.getItem('settings') || '{}');
11
12// Remove data
13localStorage.removeItem('username');
14
15// Clear all data
16localStorage.clear();
17
18// Check if key exists
19if (localStorage.getItem('username') !== null) {
20 console.log('Username is stored');
21}sessionStorage - Session Storage:
Data persists only for the browser session
1// Store temporary data
2sessionStorage.setItem('currentPage', 'dashboard');
3sessionStorage.setItem('formData', JSON.stringify({
4 name: 'John',
5 email: '[email protected]'
6}));
7
8// Retrieve session data
9const currentPage = sessionStorage.getItem('currentPage');
10const formData = JSON.parse(sessionStorage.getItem('formData') || '{}');
11
12// Session data is automatically cleared when tab closesStorage Helper Class:
1class StorageManager {
2 constructor(storageType = 'localStorage') {
3 this.storage = storageType === 'sessionStorage' ? sessionStorage : localStorage;
4 }
5
6 // Set data with optional expiration
7 set(key, value, expirationDays = null) {
8 const data = {
9 value: value,
10 timestamp: Date.now(),
11 expiration: expirationDays ? Date.now() + (expirationDays * 24 * 60 * 60 * 1000) : null
12 };
13
14 try {
15 this.storage.setItem(key, JSON.stringify(data));
16 return true;
17 } catch (error) {
18 console.error('Storage error:', error);
19 return false;
20 }
21 }
22
23 // Get data with expiration check
24 get(key) {
25 try {
26 const item = this.storage.getItem(key);
27 if (!item) return null;
28
29 const data = JSON.parse(item);
30
31 // Check expiration
32 if (data.expiration && Date.now() > data.expiration) {
33 this.storage.removeItem(key);
34 return null;
35 }
36
37 return data.value;
38 } catch (error) {
39 console.error('Storage retrieval error:', error);
40 return null;
41 }
42 }
43
44 // Remove item
45 remove(key) {
46 this.storage.removeItem(key);
47 }
48
49 // Clear all items
50 clear() {
51 this.storage.clear();
52 }
53
54 // Get all keys
55 keys() {
56 return Object.keys(this.storage);
57 }
58
59 // Check storage quota
60 checkQuota() {
61 try {
62 const testKey = '__storage_test__';
63 const testValue = 'test';
64 this.storage.setItem(testKey, testValue);
65 this.storage.removeItem(testKey);
66 return true;
67 } catch (error) {
68 return false;
69 }
70 }
71}
72
73// Usage
74const storage = new StorageManager('localStorage');
75
76// Store data with 7-day expiration
77storage.set('userPreferences', { theme: 'dark' }, 7);
78
79// Retrieve data
80const preferences = storage.get('userPreferences');
81
82// Check if storage is available
83if (storage.checkQuota()) {
84 console.log('Storage is available');
85}IndexedDB is a powerful client-side database for storing large amounts of structured data, including files and blobs.
Basic IndexedDB Operations:
1class IndexedDBManager {
2 constructor(dbName, version = 1) {
3 this.dbName = dbName;
4 this.version = version;
5 this.db = null;
6 }
7
8 // Initialize database
9 async init() {
10 return new Promise((resolve, reject) => {
11 const request = indexedDB.open(this.dbName, this.version);
12
13 request.onerror = () => reject(request.error);
14 request.onsuccess = () => {
15 this.db = request.result;
16 resolve(this.db);
17 };
18
19 request.onupgradeneeded = (event) => {
20 const db = event.target.result;
21
22 // Create object stores
23 if (!db.objectStoreNames.contains('users')) {
24 const userStore = db.createObjectStore('users', { keyPath: 'id', autoIncrement: true });
25 userStore.createIndex('email', 'email', { unique: true });
26 userStore.createIndex('name', 'name', { unique: false });
27 }
28
29 if (!db.objectStoreNames.contains('posts')) {
30 const postStore = db.createObjectStore('posts', { keyPath: 'id', autoIncrement: true });
31 postStore.createIndex('userId', 'userId', { unique: false });
32 postStore.createIndex('timestamp', 'timestamp', { unique: false });
33 }
34 };
35 });
36 }
37
38 // Add data
39 async add(storeName, data) {
40 return new Promise((resolve, reject) => {
41 const transaction = this.db.transaction([storeName], 'readwrite');
42 const store = transaction.objectStore(storeName);
43 const request = store.add(data);
44
45 request.onsuccess = () => resolve(request.result);
46 request.onerror = () => reject(request.error);
47 });
48 }
49
50 // Get data by key
51 async get(storeName, key) {
52 return new Promise((resolve, reject) => {
53 const transaction = this.db.transaction([storeName], 'readonly');
54 const store = transaction.objectStore(storeName);
55 const request = store.get(key);
56
57 request.onsuccess = () => resolve(request.result);
58 request.onerror = () => reject(request.error);
59 });
60 }
61
62 // Get all data
63 async getAll(storeName) {
64 return new Promise((resolve, reject) => {
65 const transaction = this.db.transaction([storeName], 'readonly');
66 const store = transaction.objectStore(storeName);
67 const request = store.getAll();
68
69 request.onsuccess = () => resolve(request.result);
70 request.onerror = () => reject(request.error);
71 });
72 }
73
74 // Update data
75 async update(storeName, data) {
76 return new Promise((resolve, reject) => {
77 const transaction = this.db.transaction([storeName], 'readwrite');
78 const store = transaction.objectStore(storeName);
79 const request = store.put(data);
80
81 request.onsuccess = () => resolve(request.result);
82 request.onerror = () => reject(request.error);
83 });
84 }
85
86 // Delete data
87 async delete(storeName, key) {
88 return new Promise((resolve, reject) => {
89 const transaction = this.db.transaction([storeName], 'readwrite');
90 const store = transaction.objectStore(storeName);
91 const request = store.delete(key);
92
93 request.onsuccess = () => resolve();
94 request.onerror = () => reject(request.error);
95 });
96 }
97
98 // Search by index
99 async searchByIndex(storeName, indexName, value) {
100 return new Promise((resolve, reject) => {
101 const transaction = this.db.transaction([storeName], 'readonly');
102 const store = transaction.objectStore(storeName);
103 const index = store.index(indexName);
104 const request = index.get(value);
105
106 request.onsuccess = () => resolve(request.result);
107 request.onerror = () => reject(request.error);
108 });
109 }
110}
111
112// Usage
113async function demonstrateIndexedDB() {
114 const dbManager = new IndexedDBManager('MyAppDB', 1);
115 await dbManager.init();
116
117 // Add user
118 const userId = await dbManager.add('users', {
119 name: 'John Doe',
120 email: '[email protected]',
121 createdAt: new Date().toISOString()
122 });
123
124 // Add post
125 await dbManager.add('posts', {
126 userId: userId,
127 title: 'My First Post',
128 content: 'This is the content of my first post.',
129 timestamp: Date.now()
130 });
131
132 // Get user
133 const user = await dbManager.get('users', userId);
134 console.log('Retrieved user:', user);
135
136 // Search by email
137 const userByEmail = await dbManager.searchByIndex('users', 'email', '[email protected]');
138 console.log('User found by email:', userByEmail);
139
140 // Get all posts
141 const posts = await dbManager.getAll('posts');
142 console.log('All posts:', posts);
143}
144
145demonstrateIndexedDB();Cookies are small pieces of data stored by the browser and sent with every HTTP request to the same domain.
Cookie Management:
1class CookieManager {
2 // Set cookie with options
3 static set(name, value, options = {}) {
4 let cookieString = encodeURIComponent(name) + '=' + encodeURIComponent(value);
5
6 // Add expiration
7 if (options.expires) {
8 if (options.expires instanceof Date) {
9 cookieString += '; expires=' + options.expires.toUTCString();
10 } else {
11 // Assume days
12 const date = new Date();
13 date.setDate(date.getDate() + options.expires);
14 cookieString += '; expires=' + date.toUTCString();
15 }
16 }
17
18 // Add max age
19 if (options.maxAge) {
20 cookieString += '; max-age=' + options.maxAge;
21 }
22
23 // Add domain
24 if (options.domain) {
25 cookieString += '; domain=' + options.domain;
26 }
27
28 // Add path
29 if (options.path) {
30 cookieString += '; path=' + options.path;
31 }
32
33 // Add secure flag
34 if (options.secure) {
35 cookieString += '; secure';
36 }
37
38 // Add httpOnly flag (server-side only)
39 if (options.httpOnly) {
40 cookieString += '; httponly';
41 }
42
43 // Add SameSite attribute
44 if (options.sameSite) {
45 cookieString += '; samesite=' + options.sameSite;
46 }
47
48 document.cookie = cookieString;
49 }
50
51 // Get cookie value
52 static get(name) {
53 const encodedName = encodeURIComponent(name);
54 const cookies = document.cookie.split(';');
55
56 for (let cookie of cookies) {
57 cookie = cookie.trim();
58 if (cookie.startsWith(encodedName + '=')) {
59 return decodeURIComponent(cookie.substring(encodedName.length + 1));
60 }
61 }
62
63 return null;
64 }
65
66 // Remove cookie
67 static remove(name, options = {}) {
68 this.set(name, '', {
69 ...options,
70 expires: new Date(0)
71 });
72 }
73
74 // Get all cookies
75 static getAll() {
76 const cookies = {};
77 document.cookie.split(';').forEach(cookie => {
78 cookie = cookie.trim();
79 const [name, value] = cookie.split('=');
80 if (name && value) {
81 cookies[decodeURIComponent(name)] = decodeURIComponent(value);
82 }
83 });
84 return cookies;
85 }
86
87 // Check if cookies are enabled
88 static isEnabled() {
89 this.set('test_cookie', 'test', { maxAge: 1 });
90 const enabled = this.get('test_cookie') === 'test';
91 this.remove('test_cookie');
92 return enabled;
93 }
94}
95
96// Usage examples
97CookieManager.set('user_preference', 'dark_theme', {
98 expires: 30, // 30 days
99 path: '/',
100 secure: true,
101 sameSite: 'strict'
102});
103
104const theme = CookieManager.get('user_preference');
105console.log('User theme:', theme);
106
107// Session cookie (expires when browser closes)
108CookieManager.set('session_id', 'abc123', {
109 path: '/',
110 secure: true,
111 sameSite: 'strict'
112});
113
114// Remove cookie
115CookieManager.remove('user_preference');Storage Comparison and Best Practices:
1// Storage capacity and limitations
2const storageInfo = {
3 localStorage: {
4 capacity: '5-10MB',
5 persistence: 'Until manually cleared',
6 scope: 'Origin (protocol + domain + port)',
7 accessibility: 'JavaScript only',
8 bestFor: 'User preferences, app state'
9 },
10
11 sessionStorage: {
12 capacity: '5-10MB',
13 persistence: 'Session only',
14 scope: 'Tab/window',
15 accessibility: 'JavaScript only',
16 bestFor: 'Temporary data, form state'
17 },
18
19 indexedDB: {
20 capacity: '50MB+ (quota managed)',
21 persistence: 'Until manually cleared',
22 scope: 'Origin',
23 accessibility: 'JavaScript only',
24 bestFor: 'Large datasets, offline apps'
25 },
26
27 cookies: {
28 capacity: '4KB per cookie',
29 persistence: 'Configurable',
30 scope: 'Domain/path configurable',
31 accessibility: 'JavaScript + HTTP',
32 bestFor: 'Server communication, auth tokens'
33 }
34};
35
36// Storage selection helper
37function selectStorageMethod(dataSize, persistence, serverAccess) {
38 if (serverAccess && dataSize < 4000) {
39 return 'cookies';
40 }
41
42 if (dataSize > 10000000) { // > 10MB
43 return 'indexedDB';
44 }
45
46 if (persistence === 'session') {
47 return 'sessionStorage';
48 }
49
50 return 'localStorage';
51}
52
53// Usage
54const storageMethod = selectStorageMethod(
55 1000, // 1KB data
56 'permanent', // Persistent storage
57 false // No server access needed
58);
59
60console.log('Recommended storage:', storageMethod); // localStorageSecurity Considerations:
1// Secure storage practices
2class SecureStorage {
3 // Basic encryption for sensitive data
4 static encrypt(data, key) {
5 // Simple XOR encryption (use proper encryption in production)
6 const encrypted = btoa(JSON.stringify(data))
7 .split('')
8 .map((char, i) => String.fromCharCode(char.charCodeAt(0) ^ key.charCodeAt(i % key.length)))
9 .join('');
10 return btoa(encrypted);
11 }
12
13 static decrypt(encryptedData, key) {
14 try {
15 const decrypted = atob(encryptedData)
16 .split('')
17 .map((char, i) => String.fromCharCode(char.charCodeAt(0) ^ key.charCodeAt(i % key.length)))
18 .join('');
19 return JSON.parse(atob(decrypted));
20 } catch {
21 return null;
22 }
23 }
24
25 // Store sensitive data with encryption
26 static setSecure(key, data, encryptionKey) {
27 const encrypted = this.encrypt(data, encryptionKey);
28 localStorage.setItem(key, encrypted);
29 }
30
31 // Retrieve and decrypt sensitive data
32 static getSecure(key, encryptionKey) {
33 const encrypted = localStorage.getItem(key);
34 if (!encrypted) return null;
35 return this.decrypt(encrypted, encryptionKey);
36 }
37}
38
39// Security best practices
40const securityGuidelines = {
41 doNot: [
42 'Store passwords or sensitive credentials',
43 'Store unencrypted personal information',
44 'Trust client-side data without server validation',
45 'Store large amounts of data in cookies',
46 'Use localStorage for session management'
47 ],
48
49 do: [
50 'Validate and sanitize all stored data',
51 'Use HTTPS for cookie transmission',
52 'Set appropriate cookie attributes (secure, sameSite)',
53 'Implement proper data expiration',
54 'Consider data privacy regulations (GDPR, etc.)'
55 ]
56};
57
58console.log('Storage security guidelines:', securityGuidelines);Your progress is saved in this browser, on this device only.