> ## 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.

# Multi-platform builds for Chrome and Firefox

> Build and package the same extension for Chrome, Edge, and Firefox without separate build scripts. Output artifacts to dist and generate zip packages.

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:

<CodeGroup>
  ```bash npm theme={null}
  npx extension build
  ```

  ```bash pnpm theme={null}
  pnpx extension build
  ```

  ```bash yarn 2+ theme={null}
  yarn dlx extension build
  ```

  ```bash bun theme={null}
  bunx extension build
  ```

  ```bash deno theme={null}
  deno run -A npm:extension build
  ```
</CodeGroup>

The default browser target is `chromium` unless you override it.

## Browser selection

You can target a specific browser/engine:

<CodeGroup>
  ```bash npm theme={null}
  npx extension build --browser=chrome
  ```

  ```bash pnpm theme={null}
  pnpx extension build --browser=chrome
  ```

  ```bash yarn 2+ theme={null}
  yarn dlx extension build --browser=chrome
  ```

  ```bash bun theme={null}
  bunx extension build --browser=chrome
  ```

  ```bash deno theme={null}
  deno run -A npm:extension build --browser=chrome
  ```
</CodeGroup>

<CodeGroup>
  ```bash npm theme={null}
  npx extension build --browser=firefox
  ```

  ```bash pnpm theme={null}
  pnpx extension build --browser=firefox
  ```

  ```bash yarn 2+ theme={null}
  yarn dlx extension build --browser=firefox
  ```

  ```bash bun theme={null}
  bunx extension build --browser=firefox
  ```

  ```bash deno theme={null}
  deno run -A npm:extension build --browser=firefox
  ```
</CodeGroup>

Supported values include:

* `chrome`
* `edge`
* `firefox`
* `chromium`
* `chromium-based`
* `gecko-based` / `firefox-based` (aliases)

You can also run a build matrix in one command:

<CodeGroup>
  ```bash npm theme={null}
  npx extension build --browser=chrome,edge,firefox
  ```

  ```bash pnpm theme={null}
  pnpx extension build --browser=chrome,edge,firefox
  ```

  ```bash yarn 2+ theme={null}
  yarn dlx extension build --browser=chrome,edge,firefox
  ```

  ```bash bun theme={null}
  bunx extension build --browser=chrome,edge,firefox
  ```

  ```bash deno theme={null}
  deno run -A npm:extension build --browser=chrome,edge,firefox
  ```
</CodeGroup>

This builds sequentially for `chrome`, `edge`, and `firefox`.

<Frame>
  <iframe className="w-full aspect-video rounded-xl" src="https://www.youtube-nocookie.com/embed/altdCulWJ_w?rel=0" title="Extension.js: one build, three browser targets" loading="lazy" allow="encrypted-media; picture-in-picture; fullscreen" allowFullScreen />
</Frame>

## 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](/docs/browsers/browsers-available#requested-target-vs-launch-binary).

### Build capabilities

| Option | What it does |
| - | - |
| `--browser=<target>` | Builds for a specific browser or engine family. |
| `--zip` | Creates a distribution zip per target output. |
| `--zip-filename=<name>` | Names both archives. The browser is always appended to the distribution archive. |
| `--zip-source` | Creates a source archive alongside output. |
| `--polyfill` | Enables compatibility behavior for `browser.*` API usage in Chromium-family targets. |

## Generating a zip file

Generate a distribution zip from each target output with `--zip`:

<CodeGroup>
  ```bash npm theme={null}
  npx extension build --zip
  ```

  ```bash pnpm theme={null}
  pnpx extension build --zip
  ```

  ```bash yarn 2+ theme={null}
  yarn dlx extension build --zip
  ```

  ```bash bun theme={null}
  bunx extension build --zip
  ```

  ```bash deno theme={null}
  deno run -A npm:extension build --zip
  ```
</CodeGroup>

By default, the zip name uses sanitized manifest `name` + `version` + browser, for example:

* `dist/my-extension-1.0.0-chrome.zip`

Customize filename:

<CodeGroup>
  ```bash npm theme={null}
  npx extension build --zip --zip-filename=my-release
  ```

  ```bash pnpm theme={null}
  pnpx extension build --zip --zip-filename=my-release
  ```

  ```bash yarn 2+ theme={null}
  yarn dlx extension build --zip --zip-filename=my-release
  ```

  ```bash bun theme={null}
  bunx extension build --zip --zip-filename=my-release
  ```

  ```bash deno theme={null}
  deno run -A npm:extension build --zip --zip-filename=my-release
  ```
</CodeGroup>

This creates `dist/my-release-chrome.zip`, beside the `dist/<browser>` folder rather than inside it. The browser is always appended, so each target keeps its own archive even when two builds share one name.

## Include source archive

Use `--zip-source` to generate a source archive alongside distribution output.

`--zip-source` produces:

* `dist/<name>-<version>-source.zip`, or `dist/<your-name>-source.zip` when you pass `--zip-filename`. The source is the same for every browser, so one build writes one source archive and its name carries no browser.

## Firefox store readiness: data collection permissions

Since November 2025, [addons.mozilla.org](https://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:

```json theme={null}
{
  "firefox:browser_specific_settings": {
    "gecko": {
      "data_collection_permissions": {
        "required": ["none"]
      }
    }
  }
}
```

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](https://extensionworkshop.com/documentation/develop/firefox-builtin-data-consent/) 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`:

<CodeGroup>
  ```bash npm theme={null}
  npx extension build --polyfill
  ```

  ```bash pnpm theme={null}
  pnpx extension build --polyfill
  ```

  ```bash yarn 2+ theme={null}
  yarn dlx extension build --polyfill
  ```

  ```bash bun theme={null}
  bunx extension build --polyfill
  ```

  ```bash deno theme={null}
  deno run -A npm:extension build --polyfill
  ```
</CodeGroup>

## 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

* Learn more about the [Browsers available](/docs/browsers/browsers-available).
* Explore [Path resolution](/docs/features/path-resolution) for asset/output mapping details.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.