Environment variables

Use one extension codebase across browsers and environments without hardcoding values.

Manage API hosts, keys, and browser-specific values cleanly across environments. Extension.js resolves env files during compilation, injects public variables into JavaScript (process.env and import.meta.env), and replaces placeholders in emitted .json and .html assets.

How it works

Extension.js checks environment files in priority order and uses the first matching file:

  • .env.[browser].[mode]: Browser-specific environment variables for a specific mode.
  • .env.[browser]: Browser-specific environment variables.
  • .env.[mode]: Mode-specific environment variables.
  • .env.local: Local-only overrides.
  • .env: Default environment variables.
  • .env.example: Fallback when no env file above exists.

In addition, .env.defaults is loaded as default values, and system environment variables (process.env) take final precedence.

Note: File selection is single-file priority (plus .env.defaults + system env), not full cascading across all files.

extension.config.* env resolution in monorepos

When loading extension.config.js / .mjs / .cjs, Extension.js preloads env files into process.env:

  • It first resolves from the extension project directory.
  • If no .env* file exists there, it falls back to the nearest workspace root (directory containing pnpm-workspace.yaml) and applies the same priority order.

This makes submodule-style extension apps work with shared monorepo root env files without custom dotenv setup.

Built-in environment variables

Extension.js injects built-in variables at compile time, so browser and mode are always available in your extension code.

Variable NameDescription
EXTENSION_PUBLIC_BROWSERThe current browser target for your extension (e.g., chrome, firefox, edge).
EXTENSION_PUBLIC_MODEThe mode in which your extension is running, such as development or production.
EXTENSION_BROWSERBrowser target (non-legacy alias).
EXTENSION_MODEBuild mode (non-legacy alias).
BROWSERShort browser alias.
MODEShort mode alias.
NODE_ENVNode environment aligned to compiler mode (development / production).

All built-ins above are available through both process.env.* and import.meta.env.*.

Environment variable inventory

Public/runtime variables (user-defined)

Variable patternPurposeAvailable in JS runtimeNotes
EXTENSION_PUBLIC_*Expose user-defined values to extension codeYes (process.env + import.meta.env)Safe-to-ship values only

Static placeholder variables

Variable patternPurposeAvailable in JS runtimeNotes
$EXTENSION_* tokens in emitted .html / .jsonBuild-time placeholder replacement in static assetsNot as JS varsAvoid using secrets in static templates

Built-in/alias variables

VariableTypeNotes
EXTENSION_PUBLIC_BROWSERbuilt-inBrowser target
EXTENSION_PUBLIC_MODEbuilt-inBuild mode
EXTENSION_BROWSERbuilt-in aliasBrowser target
EXTENSION_MODEbuilt-in aliasBuild mode
BROWSERbuilt-in aliasShort browser name
MODEbuilt-in aliasShort mode name
NODE_ENVbuilt-inCompiler mode

CLI and dev-server operational variables

VariablePurposeTypical usage
EXTENSION_AUTO_EXIT_MSAuto-exit dev process after N msCI hard-stop control
EXTENSION_FORCE_KILL_MSForce-kill timeout fallbackCI cleanup resilience
EXTENSION_VERBOSEVerbose diagnostics in selected flowsDebugging CLI behavior
EXTENSION_AUTHOR_MODEMaintainer/author diagnostics modeInternal diagnostics and tooling

Browser-specific environment files

Need different values per browser? Extension.js supports browser-scoped env files such as .env.chrome and .env.firefox. You can also combine browser and mode for a single build variant:

  • .env.chrome.development: Applied only when running the extension in Chrome during development mode.
  • .env.firefox.production: Applied only when building the extension for Firefox in production mode.

Priority order is:

  • .env.[browser].[mode]
  • .env.[browser]
  • .env.[mode]
  • .env.local
  • .env
  • .env.example

Example files:

# .env.chrome.development
EXTENSION_PUBLIC_API_URL=https://api-dev.chrome.com
# .env.firefox.production
EXTENSION_PUBLIC_API_URL=https://api.firefox.com

Custom environment variables

You can define custom variables in env files at project root.
Only variables prefixed with EXTENSION_PUBLIC_ are injected into JavaScript bundles (process.env / import.meta.env).

# .env
EXTENSION_PUBLIC_API_KEY=your_api_key_here
EXTENSION_PUBLIC_SITE_URL=https://example.com
PRIVATE_KEY=abc123  # Not injected into JS bundles

Important: Variables without EXTENSION_PUBLIC_ are not injected into JS bundles.
However, placeholders in emitted .json/.html files can resolve $EXTENSION_* tokens, so avoid referencing secrets in static asset templates.

Using environment variables

You can use environment variables in manifest.json, locales, HTML, and JavaScript/TypeScript files.

1. In manifest.json

manifest.json does not natively support environment variables, but Extension.js replaces supported placeholders during build. For example:

{
  "name": "My Extension",
  "version": "1.0",
  "description": "This extension is connected to $EXTENSION_PUBLIC_API_KEY",
  "background": {
    "service_worker": "service_worker.js"
  }
}

During compilation, $EXTENSION_PUBLIC_API_KEY is replaced with the resolved env value.

2. In locale files

You can also use placeholders in locale files when values should change by environment. For example:

{
  "appName": {
    "message": "My Extension - $EXTENSION_PUBLIC_SITE_URL"
  },
  "appDescription": {
    "message": "Connected to API at $EXTENSION_PUBLIC_API_KEY"
  }
}

When assets are emitted, placeholders such as $EXTENSION_PUBLIC_SITE_URL are replaced with resolved values.

3. In HTML files

You can also use placeholders in static HTML files (for example, under pages/):

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>My Extension</title>
  </head>
  <body>
    <h1>Welcome to My Extension</h1>
    <p>API Key: $EXTENSION_PUBLIC_API_KEY</p>
  </body>
</html>

When built, $EXTENSION_PUBLIC_API_KEY is replaced in the output HTML.

4. In JSX components

In React/JSX/TS files, read env values with process.env:

const ApiInfo = () => {
  const apiUrl = process.env.EXTENSION_PUBLIC_SITE_URL
  const apiKey = process.env.EXTENSION_PUBLIC_API_KEY

  return (
    <div>
      <h1>API Information</h1>
      <p>URL: {apiUrl}</p>
      <p>Key: {apiKey}</p>
    </div>
  )
}

export default ApiInfo

These values are inlined at compile time and can vary by browser/mode.

import.meta support

For ESM workflows, Extension.js also supports import.meta.env:

service_worker.mjs
const apiUrl = import.meta.env.EXTENSION_PUBLIC_API_URL
console.log(`API URL for the current environment: ${apiUrl}`)

import.meta.env and process.env have parity for injected env keys.

Best practices

  • Expose only what must ship: Prefix only client-safe keys with EXTENSION_PUBLIC_.
  • Use .env.defaults for shared defaults: Keep predictable team defaults while allowing local/system overrides.
  • Keep secrets out of static placeholders: Avoid putting secret $EXTENSION_* tokens in HTML/JSON templates.
  • Version control hygiene: Commit .env.example and ignore real env files (.env, .env.local, browser/mode variants).

Next steps