Extension.js handles browser-specific setup during development and build. You produce runtime-correct outputs for Chromium and Gecko (Firefox engine) targets from one project.Documentation Index
Fetch the complete documentation index at: https://extension.js.org/llms.txt
Use this file to discover all available pages before exploring further.
Cross-browser extension development
Cross-browser extension development means writing one extension that ships to Chrome, Edge, and Firefox without forking the codebase. Extension.js compiles a single project into per-browser artifacts (dist/chrome, dist/edge, dist/firefox) and filters the manifest for the active target. The reload loop, runtime APIs, and packaging all match the browser you are debugging.
Use one manifest.json and target specific browsers or custom binaries. Enable polyfills when you need browser.* API compatibility in Chromium-family targets.
Chrome and Firefox extension compatibility
Chrome and Firefox extensions share most of the WebExtensions surface but differ in three places that matter every day:- Background scripts: Chrome (Manifest V3) requires
background.service_worker. Firefox usesbackground.scriptswith non-persistent event pages. Extension.js routes both from one prefixed manifest. - Runtime API namespace: Firefox supports
browser.*natively. Chromium useschrome.*. Pass--polyfillto bridge the gap on Chromium targets. - Permissions and content security policy (CSP): Browsers evaluate some
permissionsandhost_permissionsentries differently. Keep browser-specific values behind prefixed manifest keys instead of feature-detecting at runtime.
Browser-specific manifest fields
Browser-specific manifest fields are how Extension.js keeps a singlemanifest.json working across browsers. At compile time, Extension.js filters prefixed keys (chromium:, chrome:, edge:, firefox:, gecko:) to the active target. Unprefixed keys apply everywhere. See Browser-specific manifest fields for the full prefix list.
Template examples
action

How it works
1) Choose a browser target
Choose where you want to run your extension. Use--browser for common targets, or pass a custom browser binary.
- macOS:
/Applications/Brave Browser.app/Contents/MacOS/Brave Browser - Linux:
/usr/bin/brave-browser(or another Chromium-based browser binary) - Windows:
"C:\\Program Files\\BraveSoftware\\Brave-Browser\\Application\\brave.exe"
chromium-basedfor--chromium-binarygecko-basedfor--gecko-binary
2) Compile with browser-specific manifest filtering
During compilation, Extension.js includes only the manifest fields for your selected browser. For example:chromium:, chrome:, and edge:.For Firefox-family targets, Extension.js uses
firefox: and gecko:.
See Browser-specific manifest fields
for full details on prefixed manifest fields.
3) Output per browser target
Extension.js writes each target to its own build folder:dist/chromedist/edgedist/firefoxdist/chromium-based(custom Chromium engines)dist/gecko-based(custom Gecko engines)
4) Optional browser.* polyfill for Chromium targets
If your code uses browser.*, enable --polyfill for Chromium-family targets:
webextension-polyfill (Mozilla’s browser.* compatibility library) for non-Firefox targets.Extension.js skips this step for Firefox because Firefox already supports
browser.* natively.
Best practices
- Keep one codebase: Put browser differences in prefixed manifest fields when possible.
- Build one target at a time: Generate dedicated artifacts (
dist/<browser>) for each browser in continuous integration (CI). - Use
--polyfillwhen needed: Enable it only when your code depends onbrowser.*in Chromium-family targets. - Check API support early: Use MDN WebExtensions docs before relying on browser-specific APIs.
Next steps
- Learn more about the Browsers available.
- Learn more about Browser-specific manifest fields.

