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
Β
Overview: Three Types of Extension Storage
Browser extensions have three primary storage APIs, each designed for specific use cases:
-
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
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: Device-Specific Storage
What is chrome.storage.local?
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.
Key Characteristics
-
Storage Limit: 10 MB (Chrome allows unlimited with
unlimitedStoragepermission) - 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
Usage Example (Async/Await - Modern)
// 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);
}
}
Usage Example (Promise-Based - Legacy)
// 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);
});
}
Chrome Storage Sync: Cross-Device Synchronization
What is chrome.storage.sync?
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.
Key Characteristics
- 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
Usage Example (Async/Await - Modern)
// 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);
}
}
Usage Example (Promise-Based - Legacy)
// 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);
});
}
Chrome Storage Session: Temporary Session Data
What is chrome.storage.session?
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.
Key Characteristics
- 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
-
Access Control: By default, not exposed to content scripts (can be changed with
setAccessLevel()) - Best For: Temporary state, session-specific data, runtime variables
Usage Example (Async/Await - Modern)
// 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);
}
}
Usage Example (Promise-Based - Legacy)
// 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);
});
}
Storage Comparison Table
| Feature | Local Storage | Sync Storage | Session Storage |
|---|---|---|---|
| Storage Limit | 10 MB (unlimited with permission) | 100 KB | 10 MB |
| Syncs Across Devices | No | Yes | No |
| Persistence | Until deleted | Until deleted | Session only |
| Chrome Support | MV2 & MV3 | MV2 & MV3 | MV3 |
| Firefox Support | MV2 & MV3 | MV2 & MV3 | MV2 & MV3 |
| Best Use Case | Large data, cache | User preferences | Temporary state |
| Speed | Fast | Slower (sync overhead) | Fast (in-memory) |
Chrome vs Firefox: Key Differences
Browser Namespace Compatibility
Both Chrome and Firefox support similar storage APIs, but Firefox uses a different namespace:
// 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' });
Manifest Configuration Examples
Firefox Manifest V2:
{
"manifest_version": 2,
"name": "My Extension",
"version": "1.0",
"permissions": ["storage"], π
"background": {
"scripts": ["background.js"],
"persistent": true
}
}
Chrome Manifest V3:
{
"manifest_version": 3,
"name": "My Extension",
"version": "1.0",
"permissions": ["storage"], π
"background": {
"service_worker": "background.js"
}
}
Firefox Manifest V3:
{
"manifest_version": 3,
"name": "My Extension",
"version": "1.0",
"permissions": ["storage"], π
"background": {
"scripts": ["background.js"]
}
}
Listening to Storage Changes
Monitor storage changes across all extension contexts:
Async/Await Example
// 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);
});
}
2. Handle Storage Quota
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);
}
}
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 π
bye bye :)
Top comments (0)