Skip to main content
Build and package the same extension for multiple browsers with predictable outputs. Ship the same extension to Chrome, Edge, and Firefox without maintaining separate build scripts. Extension.js runs production builds per target and writes artifacts to dist/<browser>. It can also generate zip packages for distribution.

How it works

Run a production build:
The default browser target is chromium unless you override it.

Browser selection

You can target a specific browser/engine:
Supported values include:
  • chrome
  • edge
  • firefox
  • chromium
  • chromium-based
  • gecko-based / firefox-based (aliases)
You can also run a build matrix in one command:
This builds sequentially for chrome, edge, and firefox.

Output layout

Each target writes to its own folder:
  • dist/chrome
  • dist/edge
  • dist/firefox
  • dist/chromium
  • dist/chromium-based
  • dist/gecko-based
The folder is always named after the requested target, even when dev launches a different Chromium-family binary as a fallback. See Requested target vs. launch binary.

Build capabilities

Generating a zip file

Generate a distribution zip from each target output with --zip:
By default, the zip name uses sanitized manifest name + version, for example:
  • my-extension-1.0.0.zip
Customize filename:
This creates my-release.zip inside the target dist/<browser> folder.

Include source archive

Use --zip-source to generate a source archive alongside distribution output. --zip-source produces:
  • dist/<name>-<version>-source.zip

Firefox store readiness: data collection permissions

Since November 2025, addons.mozilla.org rejects every new add-on whose manifest lacks browser_specific_settings.gecko.data_collection_permissions, and Mozilla has announced the key becomes mandatory for all extensions during 2026. Extensions that transmit no data must still declare it explicitly:
New projects scaffolded with extension create include this declaration, and the firefox: prefix keeps it out of Chromium builds. A production Firefox build warns when the resolved manifest lacks the key, so a missing declaration surfaces at build time instead of at submission review. Declaring "none" when the extension does transmit user data is an AMO policy violation. If your extension collects or transmits anything, declare the matching categories instead. See Mozilla’s built-in data consent documentation for the category list. Firefox parses the key from version 140 on desktop (142 on Android) and it is safe on both Manifest V2 and V3.

Polyfilling browser APIs

If your code relies on Gecko-style browser.* APIs and you need Chromium compatibility, enable --polyfill:

Best practices

  • Build per target in continuous integration (CI): Treat each browser output as an independent artifact.
  • Use a browser matrix command for parity checks: Catch target-specific issues early in one pipeline step.
  • Package intentionally: Use --zip for store uploads and --zip-source for traceable source artifacts.
  • Keep target configuration explicit: Use extension.config.* command/browser defaults for reproducible builds.

Next steps