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

# Browser-specific manifest fields

> Define Chrome, Firefox, and Edge manifest values inline with browser prefixes. Extension.js emits only the fields matching each target at build time.

<AvatarBrowsers browsers={["chromium", "firefox"]} />

Avoid maintaining separate manifest files for every browser.

Extension.js lets you define browser-specific values inline with prefixes, then emits only the fields that match the active target at compile time.

## Why this matters

Browsers still differ in key manifest areas, like background configuration and vendor metadata. Prefixed fields let you keep one source `manifest.json` while producing browser-correct output for Chromium-family and Firefox-family targets.

## How it works

Extension.js scans manifest keys and resolves prefixed entries for the selected browser. Resolution is per engine family, not per vendor:

* Chromium-family targets (`chromium`, `chrome`, `edge`, `chromium-based`, the forks `brave`, `opera`, `vivaldi`, `yandex`, and Safari builds) resolve: `chromium:`, `chrome:`, `edge:`
* Gecko-family targets (`firefox`, `gecko-based`, and the forks `waterfox`, `librewolf`) resolve: `firefox:`, `gecko:`

When a prefixed key matches the active target, Extension.js rewrites it to the unprefixed key in the emitted manifest. Fork targets inherit their engine family's prefixes, so a manifest that only carries `chromium:`/`firefox:` keys still resolves correctly when you target a fork like `brave` or `waterfox`. An exact browser-name prefix also matches its own target (for example, `brave:` when you run `--browser=brave`).

### For Chromium-based browsers (Chrome, Edge, ...)

```json theme={null}
{
  "chromium:background": {
    "service_worker": "sw.js"
  }
}
```

### For Firefox

```json theme={null}
{
  "firefox:background": {
    "scripts": ["sw.js"]
  }
}
```

This makes `service_worker` available only for Chromium-family outputs while keeping `background.scripts` for Firefox outputs.

Supported prefix map:

| Prefix      | Included for target browser                                                                        |
| ----------- | -------------------------------------------------------------------------------------------------- |
| `chromium:` | Every Chromium-family target: `chromium`, `chrome`, `edge`, `chromium-based`, forks, Safari builds |
| `chrome:`   | Every Chromium-family target (same list as `chromium:`)                                            |
| `edge:`     | Every Chromium-family target (same list as `chromium:`)                                            |
| `firefox:`  | `firefox`, `gecko-based`, `firefox-based`, `waterfox`, `librewolf`                                 |
| `gecko:`    | `firefox`, `gecko-based`, `firefox-based`, `waterfox`, `librewolf`                                 |
| `safari:`   | `safari` and `webkit-based`; wins over chromium-family keys on those targets                       |
| `webkit:`   | `safari` and `webkit-based` (same behavior as `safari:`)                                           |

An exact browser-name prefix (for example, `brave:`, `vivaldi:`, or `waterfox:`) additionally resolves only when you target that same browser, and wins over its family prefix (`chrome:` beats `chromium:` when targeting `chrome`).

Safari builds inherit the Chromium family (the converter consumes a Chrome-shaped manifest), so `chromium:`/`chrome:`/`edge:` keys apply to Safari too; use `safari:` (or `webkit:`) for Safari-only overrides — they take precedence over the family keys.

This works for any manifest field at any level, including `permissions`, `content_scripts`, and `background`.

### Family-wide resolution, not per vendor

`chromium:`, `chrome:`, and `edge:` are interchangeable within the Chromium family: each of them applies to every Chromium-family target. Building for `chrome` versus `chromium` therefore emits an identical manifest; only the `dist/<browser>` folder name and the preferred launch binary differ. If you need a field for one vendor only, use its exact browser-name prefix (for example, `brave:`), which resolves only for that target.

When two matching prefixes set the same field, the last one in source order wins:

```json theme={null}
{
  "chrome:action": {
    "default_title": "First"
  },
  "edge:action": {
    "default_title": "Second"
  }
}
```

Both prefixes match any Chromium-family target, so every Chromium-family build (including `chrome`) emits `"default_title": "Second"`.

A matching prefixed key always overrides a plain key with the same name, regardless of where each appears in the file.

## Best practices

* **Keep shared defaults unprefixed**: Put common fields in regular manifest keys, then prefix only browser-specific differences.
* **Prefix only when behavior diverges**: Use browser prefixes when runtime requirements differ.
* **Build per target in continuous integration (CI)**: Generate and verify each browser output (`dist/<browser>`) to catch compatibility regressions early.
* **Validate with MDN**: Use [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions) to confirm support before adding browser-only settings.

## Next steps

* Learn more about the [Browsers available](/docs/browsers/browsers-available).
* Learn more about [Cross-browser compatibility](/docs/features/cross-browser-compatibility).
