The chrome browser iconThe firefox browser iconThe edge browser icon

Cross-browser compatibility

Extension.js handles browser-specific setup during development and build, so the same project can produce runtime-correct outputs across Chromium and Gecko targets.

Use one manifest.json, target specific browsers or custom binaries, and enable polyfills when needed for browser.* API compatibility in Chromium-family targets.

How it works

1) Select a browser target

Choose where you want to run your extension.

Use --browser for common targets, or pass a custom browser binary.

npm
pnpm
yarn
extension dev --browser firefox
npm
pnpm
yarn
extension dev --chromium-binary /Applications/Brave\ Browser.app/Contents/MacOS/Brave\ Browser

Use a binary path that matches your OS:

  • 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"

Video demo soon: selecting a browser target

When you pass a binary, Extension.js maps it to a browser engine target:

  • chromium-based for --chromium-binary
  • gecko-based for --gecko-binary

2) Compile with browser-specific manifest filtering

During compilation, only manifest fields for the selected browser are included.

For example:

{
  "chromium:background": {
    "service_worker": "sw.js"
  },
  "firefox:background": {
    "scripts": ["sw.js"]
  }
}

Video demo soon: manifest filtering by target

For Chromium-family targets, Extension.js uses prefixes like chromium:, chrome:, and edge:.
For Firefox-family targets, Extension.js uses firefox: and gecko:.

Note: Browser-prefixed manifest fields are covered in detail in Browser-Specific Manifest Fields.

3) Output per browser target

Each target is written to its own build folder:

  • dist/chrome
  • dist/edge
  • dist/firefox
  • dist/chromium-based (custom Chromium engines)
  • dist/gecko-based (custom Gecko engines)

Video demo soon: browser-specific build output

This keeps builds organized and easier to ship in CI.

4) Optional browser.* polyfill for Chromium targets

If your code uses browser.*, enable --polyfill for Chromium-family targets:

npm
pnpm
yarn
extension build --browser chrome --polyfill

Video demo soon: polyfill flow on Chromium

When enabled, Extension.js uses webextension-polyfill for non-Firefox targets.
For Firefox, this step is skipped because browser.* is already built in.

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 CI.
  • Use --polyfill when needed: Enable it only when your code depends on browser.* in Chromium-family targets.
  • Check API support early: Use MDN WebExtensions docs before relying on browser-specific APIs.

Next steps