HTML

Build extension UIs with plain HTML entrypoints while Extension.js handles asset wiring, script/style bundling, and dev-time update behavior.

Extension.js processes HTML entrypoints from manifest fields and from the special pages/ folder.

HTML capabilities

CapabilityWhat it gives you
Entrypoint discoveryCompile HTML declared in manifest and pages/ with one flow
Asset wiringBundle referenced scripts and styles from HTML tags
Public path handlingKeep intentional public-root assets stable
Dev trackingRecompile on HTML and referenced asset edits

Supported HTML entrypoints

The following manifest fields use HTML files as entrypoints:

Manifest fieldFile type expected
action.default_popup.html
background.page.html
chrome_url_overrides.*.html
devtools_page.html
options_ui.page / options_page.html
page_action.default_popup.html
sandbox.pages.html
side_panel.default_path.html
sidebar_action.default_panel.html

You can also add standalone pages under pages/ without expanding manifest entrypoint fields for every file.

Sample HTML entry

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>My Extension Page</title>
    <link rel="stylesheet" href="./styles.css" />
  </head>
  <body>
    <div id="app"></div>
    <script src="./main.ts"></script>
  </body>
</html>

How Extension.js processes HTML

For each HTML entrypoint, Extension.js:

  1. emits the HTML file into compilation
  2. discovers local scripts/styles/static assets from tags
  3. bundles scripts/styles as proper extension outputs
  4. patches HTML to inject compiled bundle references
  5. keeps public-root references (like /icon.png) as public assets

Development behavior

  • HTML and referenced assets are tracked in file dependencies, so edits trigger recompilation.
  • Script changes use HMR where supported in the injected script flow.
  • If the HTML script/style entry list changes (add/remove entries), Extension.js can require a dev server restart.
  • Manifest HTML entrypoint changes can also trigger restart-required diagnostics.

Paths and environment variables

  • Relative paths are resolved from the HTML file location.
  • Leading / paths are treated as extension public-root paths.
  • $EXTENSION_PUBLIC_* and related placeholders can be replaced in emitted HTML during compilation.

Using extra pages

Place extra files in pages/:

pages/
└── extra-page.html

Common extension surfaces

Most user-facing extension surfaces that render UI are just HTML entrypoints with different manifest fields and runtime expectations. Use the surface that matches the user interaction model, then keep the HTML page itself thin.

SurfaceManifest fieldGood fit
Popupaction.default_popup or browser_action.default_popupshort, task-focused UI opened from the toolbar
Options pageoptions_ui.page or options_pagedurable settings and preferences
Side panelside_panel.default_path or sidebar_action.default_panelpersistent companion UI alongside the current tab
DevTools pagedevtools_pagedebugging or inspection tools for developers
New tab overridechrome_url_overrides.newtabfull-screen extension-owned browser surface
Sandbox pagesandbox.pagesisolated HTML when sandboxed execution is required

Use a popup for fast, focused actions:

  • quick status checks
  • one-step commands
  • small forms or toggles

Keep popups small and resilient. They are often opened and closed frequently, so avoid assuming long-lived in-memory UI state.

Options page

Use an options page when the user needs to configure durable behavior:

  • account or integration settings
  • feature toggles
  • keyboard or workflow preferences

Pair options pages with browser storage so settings survive restarts and can be read by background or content-script code.

Side panel

Use a side panel when the extension needs a persistent workspace next to the current page:

  • research or note-taking companions
  • tab-aware assistants
  • page analysis results that should stay visible while the user navigates

Side panel HTML is still just an extension page entrypoint. The difference is the surface semantics, not the build pipeline.

DevTools page

Use devtools_page when the extension provides tooling for developers:

  • inspection panels
  • request or DOM diagnostics
  • project-specific debugging helpers

Treat this as a specialized surface. It usually needs stronger messaging patterns because it often coordinates with background logic and inspected tabs.

New tab override

Use chrome_url_overrides.newtab when the extension owns the whole new-tab experience.

This is usually the best fit for rich, app-like UIs where a popup would be too constrained.

Sandbox page

Use sandbox pages only when the feature truly needs the sandbox model. They are HTML entrypoints too, but they exist for a narrower set of browser-extension security and execution constraints.

Surface authoring pattern

A good default structure for any HTML surface is:

  1. keep the HTML file minimal
  2. mount one script entry
  3. import styles from that script or a linked stylesheet
  4. move browser API coordination into dedicated modules

That pattern works well for popup, options, side panel, devtools, and new-tab surfaces.

Best practices

  • Keep entry HTML minimal and import app code through scripts.
  • Use relative paths for project assets and / paths only for intentional public assets.
  • Use pages/ for additional extension pages that are not primary manifest UI entrypoints.
  • Treat script/link add/remove changes as structural changes that may require restart.
  • Pick the surface by user interaction model first, then implement it as a thin HTML page entrypoint.
  • Keep surface-specific state in storage or background coordination instead of assuming the UI page always stays mounted.

Next steps