When I built the options page for Promo Drop, I wanted three toggles, instant saves, and settings that follow the user to every Chrome they sign in to. No React, no build step. Here is the whole pattern.
1. Declare the page in the manifest
"options_ui": { "page": "options.html", "open_in_tab": true },
"permissions": ["storage"]
storage is the only permission this page needs.
2. One source of defaults
const DEFAULTS = { showPanel: true, showCashback: true, compactMode: false };
Keep this in one place and pass it to every get call, so a first-time user never sees undefined.
3. Load the saved values
document.addEventListener('DOMContentLoaded', async () => {
const settings = await chrome.storage.sync.get(DEFAULTS);
for (const [key, value] of Object.entries(settings)) {
const box = document.getElementById(key);
if (box) box.checked = value;
}
});
Passing the defaults object as the key means Chrome fills in anything missing for you.
4. Save on change, not on a Save button
document.addEventListener('change', (e) => {
if (e.target.matches('input[type="checkbox"]')) {
chrome.storage.sync.set({ [e.target.id]: e.target.checked });
}
});
One listener covers every toggle, and users never lose a change because they forgot to click Save.
5. React to changes everywhere else
chrome.storage.onChanged.addListener((changes, area) => {
if (area === 'sync' && changes.showPanel) {
togglePanel(changes.showPanel.newValue);
}
});
Your content script updates live, in every open tab, the moment the user flips a switch.
Things that caught me out
-
storage.synchas quotas (about 100 KB total and 8 KB per item). Settings fit easily; anything bigger belongs instorage.local. - The sync write rate is limited, so never call
setinside a slider'sinputevent. Usechange. - Test with a second Chrome profile signed in to the same account to watch sync happen.
That is the entire options page: one HTML file, about 30 lines of JavaScript, and nothing to build. The extension it belongs to lists tested Whop promo codes; you can see it at promodrop.codes.
Top comments (0)