Skip to main content
Extension.js processes HTML entrypoints from two sources: manifest fields and the special pages/ folder. Manifest fields include popup, options, side panel, new tab, and DevTools. Use pages/ for routes the manifest does not name directly. Extension.js walks each entry for scripts, styles, and linked assets. You author plain HTML and Extension.js configures everything automatically.

Template examples

new

new template screenshot Minimal new-tab extension with an HTML page entrypoint.
npx extension@latest create my-extension --template=new
Repository: extension-js/examples/new sidebar template screenshot Side panel extension with an HTML page entry for persistent companion UI.
npx extension@latest create my-extension --template=sidebar
Repository: extension-js/examples/sidebar

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

  • Extension.js tracks HTML and referenced assets in file dependencies, so edits trigger recompilation.
  • Script changes use hot module replacement (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.
  • Changing manifest HTML entrypoints can also trigger restart-required diagnostics.

Paths and environment variables

  • Extension.js resolves relative paths from the HTML file location.
  • Extension.js treats leading / paths as extension public-root paths.
  • Extension.js replaces $EXTENSION_PUBLIC_* and related placeholders 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 your extension
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. Users open and close them frequently, so avoid assuming long-lived in-memory UI state.

Options page

Use an options page when you need 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 you navigate
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.
  • Choose 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