Chrome Extension Options Page — How to Build a Settings UI

10 min read

Chrome Extension Options Page — How to Build a Settings UI

The options page is one of the most critical components of any Chrome extension. It’s where users configure their preferences, customize behavior, and control how your extension interacts with their browsing experience. A well-designed options page improves user satisfaction, reduces support requests, and makes your extension feel professional and polished. This guide covers everything you need to know to build a production-quality options page, from choosing the right layout to implementing robust storage patterns.

Embedded vs Full-Page Options

Chrome extensions support two distinct options page configurations, each with its own use cases and trade-offs. Understanding when to use each type will help you make the right decision for your extension.

Embedded options render directly within the Chrome extensions management page. They load quickly and feel integrated with the browser’s UI, but they’re constrained to a smaller viewport and have limited styling options. Use embedded options when your settings are simple and few—think boolean toggles, dropdown selections, or single-input forms. The embedded approach works well for utility extensions that need quick configuration without distracting users from their workflow.

Full-page options open in a new browser tab, giving you complete control over the layout, styling, and user experience. This approach is ideal for complex settings interfaces with multiple sections, custom theming, or advanced form controls. Full-page options can include scrolling, complex layouts, and rich interactive elements that would feel cramped in an embedded view.

Configuring either type requires updates to your manifest.json. For embedded options, use the simple options_page property:

{
  "options_page": "options.html"
}

For full-page options with modern Manifest V3, use the more flexible options_ui object:

{
  "options_ui": {
    "page": "options.html",
    "open_in_tab": true
  }
}

The open_in_tab: true setting gives users a dedicated tab, while removing it embeds the options within Chrome’s extensions management page. Most developers prefer open_in_tab: true for complex settings, as it provides a better user experience and avoids the constrained iframe environment.

Chrome Storage Integration

The chrome.storage API is the backbone of any options page. Unlike localStorage in web pages, chrome.storage is specifically designed for extensions and offers several important advantages. It synchronizes across a user’s devices when they’re signed into Chrome (with the sync area), persists independently of the extension’s lifecycle, and provides asynchronous operations that won’t block the UI.

There are two storage areas to choose from: storage.sync and storage.local. Use storage.sync for user preferences that should follow them across devices—theme choices, notification settings, and personalized configurations. This area has a quota of about 100KB total but ensures users have consistent settings wherever they use Chrome. Use storage.local for larger data that doesn’t need to sync, such as cached content, large configuration objects, or data that changes frequently.

Here’s a basic pattern for initializing your extension’s storage:

// Default settings configuration
const DEFAULT_SETTINGS = {
  theme: 'system',
  notifications: true,
  autoSave: true,
  maxResults: 50,
  blockedDomains: []
};

// Load settings with defaults fallback
async function loadSettings() {
  const result = await chrome.storage.sync.get(DEFAULT_SETTINGS);
  return { ...DEFAULT_SETTINGS, ...result };
}

// Save individual setting
async function saveSetting(key, value) {
  await chrome.storage.sync.set({ [key]: value });
}

// Save multiple settings at once
async function saveSettings(settings) {
  await chrome.storage.sync.set(settings);
}

For production extensions, consider using a library like @theluckystrike/webext-storage or webext-storage to handle schema validation, type safety, and default value management. These libraries simplify storage operations and catch configuration errors early.

Form Design Patterns

Designing effective forms for extension options requires balancing functionality with simplicity. Users should be able to understand and modify settings quickly, without feeling overwhelmed by complexity.

Group related settings into sections using fieldsets or visual dividers. Common sections include General, Appearance, Advanced, and About. Each section should have a clear heading and a coherent set of related options. This organization helps users find what they need without scanning through dozens of unrelated settings.

Provide immediate feedback when users change settings. Show a subtle “Saved” indicator or visual confirmation when settings are persisted. This feedback assures users that their changes took effect and prevents confusion about whether settings were applied.

Use appropriate input types for each setting. Toggle switches work well for boolean on/off settings. Select dropdowns are perfect for mutually exclusive choices like theme selection. Sliders suit numerical ranges like volume or size preferences. Text inputs work for custom values, URLs, or API keys. Checkboxes are ideal for multi-select options where users can choose multiple items from a list.

Here’s an example of a well-structured options form section:

<section id="appearance-settings">
  <h2>Appearance</h2>
  
  <label for="theme-select">
    <span>Theme</span>
    <select id="theme-select">
      <option value="system">System Default</option>
      <option value="light">Light</option>
      <option value="dark">Dark</option>
    </select>
  </label>
  
  <label for="font-size">
    <span>Font Size</span>
    <input type="range" id="font-size" min="12" max="24" value="14">
    <span id="font-size-value">14px</span>
  </label>
  
  <label for="compact-mode">
    <input type="checkbox" id="compact-mode">
    <span>Enable compact mode</span>
  </label>
</section>

Save and Load Patterns

There are two primary approaches to saving settings: auto-save on every change, or manual save with a button. Each approach has merits depending on your use case.

Auto-save provides the most seamless experience. Users change a setting and it’s immediately persisted—no extra steps required. This pattern works well for simple preferences like theme selection or toggles. Implementing auto-save is straightforward with event listeners:

document.getElementById('theme-select').addEventListener('change', async (e) => {
  const theme = e.target.value;
  await saveSetting('theme', theme);
  showSaveIndicator();
});

document.getElementById('font-size').addEventListener('input', async (e) => {
  const size = parseInt(e.target.value, 10);
  document.getElementById('font-size-value').textContent = `${size}px`;
  await saveSetting('fontSize', size);
});

document.getElementById('compact-mode').addEventListener('change', async (e) => {
  await saveSetting('compactMode', e.target.checked);
});

Manual save with a button gives users more control over when changes are applied. This pattern is better for complex forms where multiple settings might need to be changed together, or where validation should occur before saving. It also provides an opportunity for a “Reset to Defaults” button that reverts all changes:

document.getElementById('save-button').addEventListener('click', async () => {
  const settings = {
    theme: document.getElementById('theme-select').value,
    fontSize: parseInt(document.getElementById('font-size').value, 10),
    compactMode: document.getElementById('compact-mode').checked
  };
  
  await saveSettings(settings);
  showSaveConfirmation();
});

document.getElementById('reset-button').addEventListener('click', async () => {
  if (confirm('Reset all settings to defaults?')) {
    await chrome.storage.sync.clear();
    await loadSettingsIntoForm();
    showResetConfirmation();
  }
});

Most modern extensions use auto-save because it provides a better user experience. However, always provide a visual indicator (a checkmark, toast message, or button state change) so users know their changes were saved.

Dark Mode Support

Supporting dark mode in your options page is essential for providing a polished user experience. Many users prefer dark interfaces, especially when working late or in low-light environments. Your options page should respect these preferences.

The simplest approach is to use CSS custom properties (variables) for colors, then update them based on the user’s theme preference:

:root {
  --bg-color: #ffffff;
  --text-color: #333333;
  --border-color: #e0e0e0;
  --input-bg: #f5f5f5;
}

[data-theme="dark"] {
  --bg-color: #1a1a1a;
  --text-color: #e0e0e0;
  --border-color: #333333;
  --input-bg: #2a2a2a;
}

body {
  background-color: var(--bg-color);
  color: var(--text-color);
}

Then apply the theme based on user preference and system settings:

async function applyTheme() {
  const settings = await loadSettings();
  let theme = settings.theme;
  
  // Resolve "system" preference
  if (theme === 'system') {
    theme = window.matchMedia('(prefers-color-scheme: dark)').matches 
      ? 'dark' 
      : 'light';
  }
  
  document.documentElement.setAttribute('data-theme', theme);
}

// Listen for system theme changes
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', applyTheme);

For full dark mode support, audit all your CSS to ensure proper contrast ratios and avoid hardcoded colors. Pay special attention to form inputs, borders, and interactive element states. Test your options page in both light and dark modes to catch visibility issues.

Best Practices Summary

Building a great options page requires attention to several key areas. First, always load settings when the page initializes and populate form fields accordingly. Users should see their current configuration immediately upon opening the options page. Second, validate all input before saving—check that URLs are valid, numbers are within expected ranges, and required fields are present. Third, provide clear labels and help text for complex settings so users understand what each option does. Fourth, consider adding import and export functionality so users can back up their configuration or transfer settings between devices. Finally, test your options page thoroughly across different Chrome versions, screen sizes, and accessibility settings.

A well-designed options page demonstrates attention to detail and respect for your users. Invest the time to get it right, and your extension will feel more professional and trustworthy.


Part of the Chrome Extension Guide by theluckystrike. Built at zovo.one.