Implementation of Options Page (Settings Page) for Browser Extension
Options Page is a separate extension page for managing settings. Unlike Popup, it doesn't auto-close and has full-screen space. Opens via chrome.runtime.openOptionsPage() or from browser's extension menu.
Two display modes
Full-screen page (options_page) — opens in separate browser tab. More space, familiar interface.
Browser embedded (options_ui) — opens directly in chrome://extensions/ at the bottom of extension card. Only in Chrome/Edge.
{
"manifest_version": 3,
"options_page": "options/options.html",
// OR (mutually exclusive):
"options_ui": {
"page": "options/options.html",
"open_in_tab": false
}
}
open_in_tab: false — show in chrome://extensions/. open_in_tab: true — open in new tab (like options_page).
React settings app structure
// options/App.tsx
import { useEffect, useState } from 'react';
import browser from 'webextension-polyfill';
interface Settings {
enabled: boolean;
theme: 'light' | 'dark' | 'system';
highlightColor: string;
blockedDomains: string[];
}
const defaultSettings: Settings = {
enabled: true,
theme: 'system',
highlightColor: '#fbbf24',
blockedDomains: [],
};
export function OptionsApp() {
const [settings, setSettings] = useState<Settings>(defaultSettings);
const [saved, setSaved] = useState(false);
useEffect(() => {
browser.storage.sync.get('settings').then(({ settings: stored }) => {
if (stored) setSettings({ ...defaultSettings, ...stored });
});
}, []);
async function saveSettings(updated: Settings) {
setSettings(updated);
await browser.storage.sync.set({ settings: updated });
setSaved(true);
setTimeout(() => setSaved(false), 2000);
}
return (
<div className="options">
<h1>Extension Settings</h1>
<section className="options__section">
<h2>General</h2>
<label>
<input
type="checkbox"
checked={settings.enabled}
onChange={e => saveSettings({ ...settings, enabled: e.target.checked })}
/>
Extension active
</label>
</section>
<section className="options__section">
<h2>Appearance</h2>
<label>
<span>Theme</span>
<select
value={settings.theme}
onChange={e => saveSettings({ ...settings, theme: e.target.value as Settings['theme'] })}
>
<option value="system">System</option>
<option value="light">Light</option>
<option value="dark">Dark</option>
</select>
</label>
</section>
{saved && <div className="options__saved">Settings saved</div>}
</div>
);
}
Best practices
- Use
chrome.storage.syncfor user settings (cross-device sync via Google account) - Validate inputs server-side if possible
- Show instant feedback when settings save
- Organize into logical sections
- Support keyboard navigation
Timeline
Simple options page: 2–3 days. Advanced with sync, import/export, themes: 5–7 days.







