Local vs Sync vs Session: Which Chrome Extension Storage Should You Use?

Local vs Sync vs Session: Which Chrome Extension Storage Should You Use?

Source: Dev.to

Overview: Three Types of Extension Storage ## Chrome Storage Local: Device-Specific Storage ## What is chrome.storage.local? ## Key Characteristics ## Usage Example (Async/Await - Modern) ## Usage Example (Promise-Based - Legacy) ## Chrome Storage Sync: Cross-Device Synchronization ## What is chrome.storage.sync? ## Key Characteristics ## Usage Example (Async/Await - Modern) ## Usage Example (Promise-Based - Legacy) ## Chrome Storage Session: Temporary Session Data ## What is chrome.storage.session? ## Key Characteristics ## Usage Example (Async/Await - Modern) ## Usage Example (Promise-Based - Legacy) ## Storage Comparison Table ## Chrome vs Firefox: Key Differences ## Browser Namespace Compatibility ## Manifest Configuration Examples ## Listening to Storage Changes ## Async/Await Example ## 2. Handle Storage Quota Let's talk about storing data in browser extensions. We have three options local storage, sync storage and session storage. Each one does something different, and picking the wrong one can lead to headaches down the road… One quick thing, i created a complete chrome extension boilerplate that lets you ship & monetize your extension fast (includes auth, payments, and everything you need). It’s called extFast Browser extensions have three primary storage APIs, each designed for specific use cases: Also these are different from the web's localStorage and sessionStorage APIs. Extension storage APIs work across all extension contexts (background scripts, content scripts, popups). chrome.storage.local stores data locally on the user's machine. This data persists until explicitly deleted or the extension is uninstalled. It's ideal for storing large amounts of data that don't need to sync across devices. chrome.storage.sync automatically syncs data across all devices where the user is signed into their browser account. This makes it perfect for user preferences and settings that should follow the user everywhere. chrome.storage.session stores data in memory for the duration of the browser session. Data is cleared when the browser or extension is closed, making it perfect for temporary runtime state. Both Chrome and Firefox support similar storage APIs, but Firefox uses a different namespace: Monitor storage changes across all extension contexts: yeah pretty much done now! PS: if you want to build & monetize your extension fast, you can checkout my chrome extension boilerplate: extFast thankyou soo much for reading πŸ’› Templates let you quickly answer FAQs or store snippets for re-use. Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse CODE_BLOCK: // Storing data async function saveToLocal() { try { await chrome.storage.local.set({ userPreferences: { theme: 'dark', fontSize: 16 }, cachedData: [1, 2, 3, 4, 5] }); console.log('Data saved to local storage'); } catch (error) { console.error('Error saving to local storage:', error); } } // Retrieving data async function getFromLocal() { try { const result = await chrome.storage.local.get(['userPreferences', 'cachedData']); console.log('User preferences:', result.userPreferences); console.log('Cached data:', result.cachedData); return result; } catch (error) { console.error('Error getting from local storage:', error); } } // Removing data async function removeFromLocal() { try { await chrome.storage.local.remove(['cachedData']); console.log('Cached data removed'); } catch (error) { console.error('Error removing from local storage:', error); } } // Clearing all data async function clearLocal() { try { await chrome.storage.local.clear(); console.log('All local storage cleared'); } catch (error) { console.error('Error clearing local storage:', error); } } Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: // Storing data async function saveToLocal() { try { await chrome.storage.local.set({ userPreferences: { theme: 'dark', fontSize: 16 }, cachedData: [1, 2, 3, 4, 5] }); console.log('Data saved to local storage'); } catch (error) { console.error('Error saving to local storage:', error); } } // Retrieving data async function getFromLocal() { try { const result = await chrome.storage.local.get(['userPreferences', 'cachedData']); console.log('User preferences:', result.userPreferences); console.log('Cached data:', result.cachedData); return result; } catch (error) { console.error('Error getting from local storage:', error); } } // Removing data async function removeFromLocal() { try { await chrome.storage.local.remove(['cachedData']); console.log('Cached data removed'); } catch (error) { console.error('Error removing from local storage:', error); } } // Clearing all data async function clearLocal() { try { await chrome.storage.local.clear(); console.log('All local storage cleared'); } catch (error) { console.error('Error clearing local storage:', error); } } CODE_BLOCK: // Storing data async function saveToLocal() { try { await chrome.storage.local.set({ userPreferences: { theme: 'dark', fontSize: 16 }, cachedData: [1, 2, 3, 4, 5] }); console.log('Data saved to local storage'); } catch (error) { console.error('Error saving to local storage:', error); } } // Retrieving data async function getFromLocal() { try { const result = await chrome.storage.local.get(['userPreferences', 'cachedData']); console.log('User preferences:', result.userPreferences); console.log('Cached data:', result.cachedData); return result; } catch (error) { console.error('Error getting from local storage:', error); } } // Removing data async function removeFromLocal() { try { await chrome.storage.local.remove(['cachedData']); console.log('Cached data removed'); } catch (error) { console.error('Error removing from local storage:', error); } } // Clearing all data async function clearLocal() { try { await chrome.storage.local.clear(); console.log('All local storage cleared'); } catch (error) { console.error('Error clearing local storage:', error); } } COMMAND_BLOCK: // Storing data function saveToLocal() { chrome.storage.local.set({ userPreferences: { theme: 'dark', fontSize: 16 }, cachedData: [1, 2, 3, 4, 5] }) .then(() => { console.log('Data saved to local storage'); }) .catch((error) => { console.error('Error saving to local storage:', error); }); } // Retrieving data function getFromLocal() { chrome.storage.local.get(['userPreferences', 'cachedData']) .then((result) => { console.log('User preferences:', result.userPreferences); console.log('Cached data:', result.cachedData); }) .catch((error) => { console.error('Error getting from local storage:', error); }); } // Removing data function removeFromLocal() { chrome.storage.local.remove(['cachedData']) .then(() => { console.log('Cached data removed'); }) .catch((error) => { console.error('Error removing from local storage:', error); }); } Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: // Storing data function saveToLocal() { chrome.storage.local.set({ userPreferences: { theme: 'dark', fontSize: 16 }, cachedData: [1, 2, 3, 4, 5] }) .then(() => { console.log('Data saved to local storage'); }) .catch((error) => { console.error('Error saving to local storage:', error); }); } // Retrieving data function getFromLocal() { chrome.storage.local.get(['userPreferences', 'cachedData']) .then((result) => { console.log('User preferences:', result.userPreferences); console.log('Cached data:', result.cachedData); }) .catch((error) => { console.error('Error getting from local storage:', error); }); } // Removing data function removeFromLocal() { chrome.storage.local.remove(['cachedData']) .then(() => { console.log('Cached data removed'); }) .catch((error) => { console.error('Error removing from local storage:', error); }); } COMMAND_BLOCK: // Storing data function saveToLocal() { chrome.storage.local.set({ userPreferences: { theme: 'dark', fontSize: 16 }, cachedData: [1, 2, 3, 4, 5] }) .then(() => { console.log('Data saved to local storage'); }) .catch((error) => { console.error('Error saving to local storage:', error); }); } // Retrieving data function getFromLocal() { chrome.storage.local.get(['userPreferences', 'cachedData']) .then((result) => { console.log('User preferences:', result.userPreferences); console.log('Cached data:', result.cachedData); }) .catch((error) => { console.error('Error getting from local storage:', error); }); } // Removing data function removeFromLocal() { chrome.storage.local.remove(['cachedData']) .then(() => { console.log('Cached data removed'); }) .catch((error) => { console.error('Error removing from local storage:', error); }); } CODE_BLOCK: // Storing data async function saveToSync() { try { await chrome.storage.sync.set({ theme: 'dark', language: 'en', notifications: true }); console.log('Settings synced across devices'); } catch (error) { console.error('Error saving to sync storage:', error); } } // Retrieving data async function getFromSync() { try { const result = await chrome.storage.sync.get(['theme', 'language', 'notifications']); console.log('Theme:', result.theme); console.log('Language:', result.language); console.log('Notifications:', result.notifications); return result; } catch (error) { console.error('Error getting from sync storage:', error); } } Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: // Storing data async function saveToSync() { try { await chrome.storage.sync.set({ theme: 'dark', language: 'en', notifications: true }); console.log('Settings synced across devices'); } catch (error) { console.error('Error saving to sync storage:', error); } } // Retrieving data async function getFromSync() { try { const result = await chrome.storage.sync.get(['theme', 'language', 'notifications']); console.log('Theme:', result.theme); console.log('Language:', result.language); console.log('Notifications:', result.notifications); return result; } catch (error) { console.error('Error getting from sync storage:', error); } } CODE_BLOCK: // Storing data async function saveToSync() { try { await chrome.storage.sync.set({ theme: 'dark', language: 'en', notifications: true }); console.log('Settings synced across devices'); } catch (error) { console.error('Error saving to sync storage:', error); } } // Retrieving data async function getFromSync() { try { const result = await chrome.storage.sync.get(['theme', 'language', 'notifications']); console.log('Theme:', result.theme); console.log('Language:', result.language); console.log('Notifications:', result.notifications); return result; } catch (error) { console.error('Error getting from sync storage:', error); } } COMMAND_BLOCK: // Storing data function saveToSync() { chrome.storage.sync.set({ theme: 'dark', language: 'en', notifications: true }) .then(() => { console.log('Settings synced across devices'); }) .catch((error) => { console.error('Error saving to sync storage:', error); }); } // Retrieving data function getFromSync() { chrome.storage.sync.get(['theme', 'language', 'notifications']) .then((result) => { console.log('Theme:', result.theme); console.log('Language:', result.language); console.log('Notifications:', result.notifications); }) .catch((error) => { console.error('Error getting from sync storage:', error); }); } Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: // Storing data function saveToSync() { chrome.storage.sync.set({ theme: 'dark', language: 'en', notifications: true }) .then(() => { console.log('Settings synced across devices'); }) .catch((error) => { console.error('Error saving to sync storage:', error); }); } // Retrieving data function getFromSync() { chrome.storage.sync.get(['theme', 'language', 'notifications']) .then((result) => { console.log('Theme:', result.theme); console.log('Language:', result.language); console.log('Notifications:', result.notifications); }) .catch((error) => { console.error('Error getting from sync storage:', error); }); } COMMAND_BLOCK: // Storing data function saveToSync() { chrome.storage.sync.set({ theme: 'dark', language: 'en', notifications: true }) .then(() => { console.log('Settings synced across devices'); }) .catch((error) => { console.error('Error saving to sync storage:', error); }); } // Retrieving data function getFromSync() { chrome.storage.sync.get(['theme', 'language', 'notifications']) .then((result) => { console.log('Theme:', result.theme); console.log('Language:', result.language); console.log('Notifications:', result.notifications); }) .catch((error) => { console.error('Error getting from sync storage:', error); }); } CODE_BLOCK: // Storing session data async function saveToSession() { try { await chrome.storage.session.set({ currentTab: 'dashboard', isProcessing: false, temporaryToken: 'abc123xyz' }); console.log('Session data stored'); } catch (error) { console.error('Error saving to session storage:', error); } } // Retrieving session data async function getFromSession() { try { const result = await chrome.storage.session.get(['currentTab', 'isProcessing']); console.log('Current tab:', result.currentTab); console.log('Is processing:', result.isProcessing); return result; } catch (error) { console.error('Error getting from session storage:', error); } } // Updating session data async function updateSessionState() { try { await chrome.storage.session.set({ isProcessing: true }); // Do some work... await chrome.storage.session.set({ isProcessing: false }); } catch (error) { console.error('Error updating session storage:', error); } } // Setting access level (allows content scripts to access), call this function from background script (for example on extension installed) async function enableContentScriptAccess() { try { await chrome.storage.session.setAccessLevel({ accessLevel: 'TRUSTED_AND_UNTRUSTED_CONTEXTS' }); console.log('Content scripts can now access session storage'); } catch (error) { console.error('Error setting access level:', error); } } Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: // Storing session data async function saveToSession() { try { await chrome.storage.session.set({ currentTab: 'dashboard', isProcessing: false, temporaryToken: 'abc123xyz' }); console.log('Session data stored'); } catch (error) { console.error('Error saving to session storage:', error); } } // Retrieving session data async function getFromSession() { try { const result = await chrome.storage.session.get(['currentTab', 'isProcessing']); console.log('Current tab:', result.currentTab); console.log('Is processing:', result.isProcessing); return result; } catch (error) { console.error('Error getting from session storage:', error); } } // Updating session data async function updateSessionState() { try { await chrome.storage.session.set({ isProcessing: true }); // Do some work... await chrome.storage.session.set({ isProcessing: false }); } catch (error) { console.error('Error updating session storage:', error); } } // Setting access level (allows content scripts to access), call this function from background script (for example on extension installed) async function enableContentScriptAccess() { try { await chrome.storage.session.setAccessLevel({ accessLevel: 'TRUSTED_AND_UNTRUSTED_CONTEXTS' }); console.log('Content scripts can now access session storage'); } catch (error) { console.error('Error setting access level:', error); } } CODE_BLOCK: // Storing session data async function saveToSession() { try { await chrome.storage.session.set({ currentTab: 'dashboard', isProcessing: false, temporaryToken: 'abc123xyz' }); console.log('Session data stored'); } catch (error) { console.error('Error saving to session storage:', error); } } // Retrieving session data async function getFromSession() { try { const result = await chrome.storage.session.get(['currentTab', 'isProcessing']); console.log('Current tab:', result.currentTab); console.log('Is processing:', result.isProcessing); return result; } catch (error) { console.error('Error getting from session storage:', error); } } // Updating session data async function updateSessionState() { try { await chrome.storage.session.set({ isProcessing: true }); // Do some work... await chrome.storage.session.set({ isProcessing: false }); } catch (error) { console.error('Error updating session storage:', error); } } // Setting access level (allows content scripts to access), call this function from background script (for example on extension installed) async function enableContentScriptAccess() { try { await chrome.storage.session.setAccessLevel({ accessLevel: 'TRUSTED_AND_UNTRUSTED_CONTEXTS' }); console.log('Content scripts can now access session storage'); } catch (error) { console.error('Error setting access level:', error); } } COMMAND_BLOCK: // Storing session data function saveToSession() { chrome.storage.session.set({ currentTab: 'dashboard', isProcessing: false, temporaryToken: 'abc123xyz' }) .then(() => { console.log('Session data stored'); }) .catch((error) => { console.error('Error saving to session storage:', error); }); } // Retrieving session data function getFromSession() { chrome.storage.session.get(['currentTab', 'isProcessing']) .then((result) => { console.log('Current tab:', result.currentTab); console.log('Is processing:', result.isProcessing); }) .catch((error) => { console.error('Error getting from session storage:', error); }); } Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: // Storing session data function saveToSession() { chrome.storage.session.set({ currentTab: 'dashboard', isProcessing: false, temporaryToken: 'abc123xyz' }) .then(() => { console.log('Session data stored'); }) .catch((error) => { console.error('Error saving to session storage:', error); }); } // Retrieving session data function getFromSession() { chrome.storage.session.get(['currentTab', 'isProcessing']) .then((result) => { console.log('Current tab:', result.currentTab); console.log('Is processing:', result.isProcessing); }) .catch((error) => { console.error('Error getting from session storage:', error); }); } COMMAND_BLOCK: // Storing session data function saveToSession() { chrome.storage.session.set({ currentTab: 'dashboard', isProcessing: false, temporaryToken: 'abc123xyz' }) .then(() => { console.log('Session data stored'); }) .catch((error) => { console.error('Error saving to session storage:', error); }); } // Retrieving session data function getFromSession() { chrome.storage.session.get(['currentTab', 'isProcessing']) .then((result) => { console.log('Current tab:', result.currentTab); console.log('Is processing:', result.isProcessing); }) .catch((error) => { console.error('Error getting from session storage:', error); }); } CODE_BLOCK: // Chrome (and Chromium-based browsers) chrome.storage.local.set({ key: 'value' }); // Firefox supports both namespaces browser.storage.local.set({ key: 'value' }); // Preferred in Firefox chrome.storage.local.set({ key: 'value' }); // Also works in Firefox // Cross-browser compatible approach const storageAPI = typeof browser !== 'undefined' ? browser : chrome; await storageAPI.storage.local.set({ key: 'value' }); Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: // Chrome (and Chromium-based browsers) chrome.storage.local.set({ key: 'value' }); // Firefox supports both namespaces browser.storage.local.set({ key: 'value' }); // Preferred in Firefox chrome.storage.local.set({ key: 'value' }); // Also works in Firefox // Cross-browser compatible approach const storageAPI = typeof browser !== 'undefined' ? browser : chrome; await storageAPI.storage.local.set({ key: 'value' }); CODE_BLOCK: // Chrome (and Chromium-based browsers) chrome.storage.local.set({ key: 'value' }); // Firefox supports both namespaces browser.storage.local.set({ key: 'value' }); // Preferred in Firefox chrome.storage.local.set({ key: 'value' }); // Also works in Firefox // Cross-browser compatible approach const storageAPI = typeof browser !== 'undefined' ? browser : chrome; await storageAPI.storage.local.set({ key: 'value' }); CODE_BLOCK: { "manifest_version": 2, "name": "My Extension", "version": "1.0", "permissions": ["storage"], πŸ‘ˆ "background": { "scripts": ["background.js"], "persistent": true } } Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: { "manifest_version": 2, "name": "My Extension", "version": "1.0", "permissions": ["storage"], πŸ‘ˆ "background": { "scripts": ["background.js"], "persistent": true } } CODE_BLOCK: { "manifest_version": 2, "name": "My Extension", "version": "1.0", "permissions": ["storage"], πŸ‘ˆ "background": { "scripts": ["background.js"], "persistent": true } } CODE_BLOCK: { "manifest_version": 3, "name": "My Extension", "version": "1.0", "permissions": ["storage"], πŸ‘ˆ "background": { "service_worker": "background.js" } } Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: { "manifest_version": 3, "name": "My Extension", "version": "1.0", "permissions": ["storage"], πŸ‘ˆ "background": { "service_worker": "background.js" } } CODE_BLOCK: { "manifest_version": 3, "name": "My Extension", "version": "1.0", "permissions": ["storage"], πŸ‘ˆ "background": { "service_worker": "background.js" } } CODE_BLOCK: { "manifest_version": 3, "name": "My Extension", "version": "1.0", "permissions": ["storage"], πŸ‘ˆ "background": { "scripts": ["background.js"] } } Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: { "manifest_version": 3, "name": "My Extension", "version": "1.0", "permissions": ["storage"], πŸ‘ˆ "background": { "scripts": ["background.js"] } } CODE_BLOCK: { "manifest_version": 3, "name": "My Extension", "version": "1.0", "permissions": ["storage"], πŸ‘ˆ "background": { "scripts": ["background.js"] } } COMMAND_BLOCK: // Listen to all storage areas chrome.storage.onChanged.addListener((changes, areaName) => { console.log(`Storage area '${areaName}' changed`); for (let [key, { oldValue, newValue }] of Object.entries(changes)) { console.log(`Key '${key}' changed from`, oldValue, 'to', newValue); } }); // Listen to specific storage area chrome.storage.session.onChanged.addListener((changes) => { if (changes.isProcessing) { console.log('Processing state changed:', changes.isProcessing.newValue); } }); // Firefox-specific listener using browser namespace if (typeof browser !== 'undefined') { browser.storage.session.onChanged.addListener((changes) => { console.log('Session storage changed (Firefox):', changes); }); } Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: // Listen to all storage areas chrome.storage.onChanged.addListener((changes, areaName) => { console.log(`Storage area '${areaName}' changed`); for (let [key, { oldValue, newValue }] of Object.entries(changes)) { console.log(`Key '${key}' changed from`, oldValue, 'to', newValue); } }); // Listen to specific storage area chrome.storage.session.onChanged.addListener((changes) => { if (changes.isProcessing) { console.log('Processing state changed:', changes.isProcessing.newValue); } }); // Firefox-specific listener using browser namespace if (typeof browser !== 'undefined') { browser.storage.session.onChanged.addListener((changes) => { console.log('Session storage changed (Firefox):', changes); }); } COMMAND_BLOCK: // Listen to all storage areas chrome.storage.onChanged.addListener((changes, areaName) => { console.log(`Storage area '${areaName}' changed`); for (let [key, { oldValue, newValue }] of Object.entries(changes)) { console.log(`Key '${key}' changed from`, oldValue, 'to', newValue); } }); // Listen to specific storage area chrome.storage.session.onChanged.addListener((changes) => { if (changes.isProcessing) { console.log('Processing state changed:', changes.isProcessing.newValue); } }); // Firefox-specific listener using browser namespace if (typeof browser !== 'undefined') { browser.storage.session.onChanged.addListener((changes) => { console.log('Session storage changed (Firefox):', changes); }); } CODE_BLOCK: async function checkStorageQuota() { try { // Check local storage quota const localBytes = await chrome.storage.local.getBytesInUse(); console.log(`Local storage using ${localBytes} bytes`); // Check session storage quota if (chrome.storage.session) { const sessionBytes = await chrome.storage.session.getBytesInUse(); console.log(`Session storage using ${sessionBytes} bytes`); } } catch (error) { console.error('Error checking quota:', error); } } Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: async function checkStorageQuota() { try { // Check local storage quota const localBytes = await chrome.storage.local.getBytesInUse(); console.log(`Local storage using ${localBytes} bytes`); // Check session storage quota if (chrome.storage.session) { const sessionBytes = await chrome.storage.session.getBytesInUse(); console.log(`Session storage using ${sessionBytes} bytes`); } } catch (error) { console.error('Error checking quota:', error); } } CODE_BLOCK: async function checkStorageQuota() { try { // Check local storage quota const localBytes = await chrome.storage.local.getBytesInUse(); console.log(`Local storage using ${localBytes} bytes`); // Check session storage quota if (chrome.storage.session) { const sessionBytes = await chrome.storage.session.getBytesInUse(); console.log(`Session storage using ${sessionBytes} bytes`); } } catch (error) { console.error('Error checking quota:', error); } } - Local Storage (chrome.storage.local): Stores data locally on the user's device - Sync Storage (chrome.storage.sync): Syncs data across devices when the user is signed in - Session Storage (chrome.storage.session): Temporary storage that persists only during the browser session - Storage Limit: 10 MB (Chrome allows unlimited with unlimitedStorage permission) - Persistence: Survives browser restarts - Sync: Does NOT sync across devices - Browser Support: Chrome MV2 & MV3, Firefox MV2 & MV3 - Best For: Cached data, large datasets, device-specific settings - Storage Limit: 100 KB total, 8 KB per item - Persistence: Syncs across signed-in devices - Sync Frequency: Automatic when online - Browser Support: Chrome MV2 & MV3, Firefox MV2 & MV3 - Best For: User settings, preferences, small configuration data - Storage Limit: 10 MB - Persistence: Only during browser session (cleared on browser close) - Browser Support: Chrome: Manifest V3 (Chrome 102+) Firefox: Both Manifest V2 and V3 supported - Chrome: Manifest V3 (Chrome 102+) - Firefox: Both Manifest V2 and V3 supported - Access Control: By default, not exposed to content scripts (can be changed with setAccessLevel()) - Best For: Temporary state, session-specific data, runtime variables - Chrome: Manifest V3 (Chrome 102+) - Firefox: Both Manifest V2 and V3 supported