How to Use the Storage Package to Persist Data with chrome.storage in React Vite Extensions
The storage package in jonghakseo/chrome-extension-boilerplate-react-vite provides a type-safe, promise-based wrapper around Chrome’s native chrome.storage API through the createStorage factory function.
The repository ships with a dedicated workspace package (packages/storage) that abstracts the complexity of cross-context state synchronization in Chrome extensions. Whether you are building a popup, content script, or background service worker, this utility allows you to persist data with chrome.storage using a modern React-friendly interface while maintaining full TypeScript safety.
Core Architecture
The storage layer is built around four key components that handle serialization, permission validation, and live updates across extension contexts.
| Component | Responsibility | Source File |
|---|---|---|
createStorage |
Factory function that returns a storage object with get, set, getSnapshot, and subscribe methods. Handles serialization, fallback values, and live-update listeners. |
packages/storage/lib/base/base.ts |
StorageEnum |
Enum mapping to Chrome’s storage areas: local, sync, managed, and session. |
packages/storage/lib/base/enums.ts |
BaseStorageType |
TypeScript interface defining the contract for storage instances. | packages/storage/lib/types.ts |
example-theme-storage |
Reference implementation demonstrating theme state persistence with toggle helpers. | packages/storage/lib/impl/example-theme-storage.ts |
When liveUpdate is enabled, the package registers a listener on chrome.storage.onChanged to ensure that every extension context—popup, options page, background script, or content script—receives the latest value automatically without manual polling.
Creating a Storage Instance
To persist data with chrome.storage, define your data shape and invoke createStorage with a unique key, default fallback values, and configuration options.
// src/storage/settings-storage.ts
import { createStorage, StorageEnum } from '@chrome-extension-boilerplate/storage/lib/base/index.js';
import type { BaseStorageType } from '@chrome-extension-boilerplate/storage/lib/types.js';
export type SettingsState = {
darkMode: boolean;
notificationsEnabled: boolean;
};
export const settingsStorage: BaseStorageType<SettingsState> = createStorage<SettingsState>(
'app-settings',
{ darkMode: false, notificationsEnabled: true },
{
storageEnum: StorageEnum.Local,
liveUpdate: true,
}
);
// Optional helper functions
export const toggleDarkMode = async () => {
await settingsStorage.set(prev => ({
...prev,
darkMode: !prev.darkMode,
}));
};
The createStorage function in packages/storage/lib/base/base.ts performs several critical operations:
- Permission Verification: Validates that the requested storage area is declared in
manifest.jsonbefore read/write operations. - Session Access Control: For
StorageEnum.Session, optionally callschrome.storage.session.setAccessLevelwhensessionAccessForContentScriptsis enabled. - State Resolution: The
setmethod accepts either a concrete value or an updater function(prev) => newVal, supporting both synchronous and asynchronous updaters through the internalupdateCachehelper.
Consuming Storage in React Components
Integrate the storage instance into React components using the subscribe method for real-time synchronization and get for initial hydration.
import { useEffect, useState } from 'react';
import { settingsStorage, toggleDarkMode } from '@/storage/settings-storage';
export default function SettingsPanel() {
const [settings, setSettings] = useState<SettingsState>({
darkMode: false,
notificationsEnabled: true,
});
// Initial load from chrome.storage
useEffect(() => {
settingsStorage.get().then(setSettings);
}, []);
// Subscribe to external changes (liveUpdate)
useEffect(() => {
const unsubscribe = settingsStorage.subscribe(() => {
settingsStorage.get().then(setSettings);
});
return unsubscribe;
}, []);
return (
<div>
<label>
<input
type="checkbox"
checked={settings.darkMode}
onChange={toggleDarkMode}
/>
Enable Dark Mode
</label>
<button onClick={() => settingsStorage.set({ darkMode: false, notificationsEnabled: false })}>
Reset to Defaults
</button>
</div>
);
}
Key methods available on the storage instance:
get(): Returns aPromise<D>resolving to the current stored value or fallback default.set(valueOrUpdater): Persists data viachrome.storageand notifies all subscribers. Accepts plain values or updater functions.subscribe(listener): Registers a callback invoked on any localsetcall or remotechrome.storage.onChangedevent whenliveUpdateis true.getSnapshot(): Returns the in-memory cache value synchronously, useful for non-async reads within the same execution context.
Configuration Options
The third argument to createStorage accepts an options object that controls persistence behavior and cross-context synchronization.
| Option | Type | Description | Typical Usage |
|---|---|---|---|
storageEnum |
StorageEnum |
Selects the Chrome storage area. Defaults to Local. |
StorageEnum.Sync for cloud-synced settings; StorageEnum.Session for temporary data |
liveUpdate |
boolean |
Enables automatic synchronization via chrome.storage.onChanged listeners. |
true when multiple extension pages need real-time state updates |
sessionAccessForContentScripts |
boolean |
Grants content scripts access to session storage (requires manifest permission). | true only when content scripts need StorageEnum.Session |
serialization |
{ serialize, deserialize } |
Custom transformation functions for complex data types. | { serialize: JSON.stringify, deserialize: JSON.parse } for non-primitive values |
As implemented in packages/storage/lib/base/base.ts, the package automatically handles JSON serialization by default, but custom serializers can be injected for encryption, compression, or schema validation scenarios.
Summary
- The storage package wraps
chrome.storagewith a type-safe, promise-based API centered on thecreateStoragefactory function defined inpackages/storage/lib/base/base.ts. - Live synchronization across popup, background, and content script contexts is achieved by setting
liveUpdate: true, which wires into Chrome’s nativeonChangedevents. - The updater function pattern in
set()supports React-style state updates, accepting either direct values or functions receiving the previous state. - Storage areas are selected via
StorageEnum(Local, Sync, Session, Managed) with automatic permission validation against the extension manifest.
Frequently Asked Questions
How do I migrate from direct chrome.storage calls to the storage package?
Replace imperative chrome.storage.local.get/set calls with a typed createStorage instance. Define your state interface, provide fallback defaults, and use the returned get and set methods. The package handles serialization and error boundaries automatically, whereas native API calls require manual JSON parsing and error handling.
Can content scripts access session storage using this package?
Yes, but you must enable sessionAccessForContentScripts: true in the configuration options and declare the storage permission in your manifest.json. The package automatically invokes chrome.storage.session.setAccessLevel once per extension lifetime to grant access when this option is configured.
What is the difference between get() and getSnapshot()?
get() always retrieves the latest value from chrome.storage asynchronously, ensuring consistency across extension restarts. getSnapshot() returns the in-memory cached value immediately without async overhead, making it suitable for synchronous reads during the same session where the cache is already warm.
How does liveUpdate keep extension contexts synchronized?
When liveUpdate is enabled, createStorage registers a listener on chrome.storage.onChanged in packages/storage/lib/base/base.ts. This listener fires whenever any extension context (popup, options page, or background script) modifies the stored value, triggering all local subscribers to refresh their state automatically.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →