Feb 23rd, 2026
When you’re building a Chrome extension, it’s common to want to store state in your user’s browser. For example, you might store settings for your extension.
This is natively supported via the chrome.storage API, a first-party extension API to store and retrieve data. You just request the storage permission, and then you can use commands like chrome.storage.local.get/.set to manipulate data.
Sounds simple, right?
It mostly is, but after building a handful of Chrome extensions myself (Tailcolors, AttendList), I’ve found a few pitfalls with the default approach.
The main pitfall stems from littering your code with calls to chrome.storage.local.get("key") and .set({"key": "value"}):
"key" strings scattered throughout your codebase.Can we do better?
The answer, obviously, is yes!
One day, I decided to try and solve this, and ended up with my useLocalStorage helper script (below), which I now use in all my Chrome extensions.
This helper is modelled after the React useState Hook, and lets you write code like this:
// popup.js, background.js etc.
const [getKey, setKey] = useLocalStorage("key");
let value = await getKey();
await setKey("value");
Compare the code above to the standard way of interacting with localStorage:
let value = await chrome.storage.local.get("key");
await chrome.storage.local.set({ key: "value" });
I find my helpers much neater!
This localStorage helper script has a few handy benefits which I love:
"key" string only appears once. For instance, I commonly define most of my useLocalStorage helpers in a common file (ie: utils/storage_helpers.js) and then import them throughout my extension code. This makes it easy to rename "key", avoid bugs from typos, and more.chrome.storage.local.get/.set with different storage keys. Your code naturally becomes clearer and more descriptive.If you write your Chrome Extensions with vanilla Javascript like I do, the localstorage.js helper below is plug-and-play. Just drop it into your project, import it, and start using it.
Note that If you’re using these helpers in your popup.js script, you’ll need to import popup.js as a module like so:
<!-- popup.html -->
<body>
...
<script type="module" src="./popup.js"></script>
</body>
With popup.js configured as a module, you can import the helper script like so:
// popup.js
import { useLocalStorage } from "./utils/localstorage.js";
const [getVar, setVar] = useLocalStorage("var");
...
If instead you’re using a framework like WXT with it’s own localStorage API, you can still use my helper script. You’ll just need to adapt the specific calls to match the WXT API.
For example,
chrome.storage.local.get([key]) -> storage.getItem('local:key')
// localstorage.js
// Usage:
// const [getVar, setVar] = useLocalStorage("var");
//
export function useLocalStorage(key) {
const getValue = async () => {
return new Promise((resolve) => {
chrome.storage.local.get([key], (result) => {
resolve(result[key]);
});
});
};
const setValue = async (value) => {
return new Promise((resolve, reject) => {
chrome.storage.local.set({ [key]: value }, () => {
if (chrome.runtime.lastError) {
console.log("failed to save to local storage", value);
reject(chrome.runtime.lastError);
} else {
resolve();
}
});
});
};
return [getValue, setValue];
}