Skip to main content
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, …)

For Firefox

This makes service_worker available only for Chromium-family outputs while keeping background.scripts for Firefox outputs. Supported prefix map: 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:
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 to confirm support before adding browser-only settings.

Next steps