Multiple environments
Set up development, staging, and production environment variables for the browser extension.
For the complete documentation index, see llms.txt. Prefer markdown by appending.mdto documentation URLs or sendingAccept: text/markdown.
Browser extensions are shipped as static bundles, so any value exposed to the extension can be inspected by users. Treat extension environment variables as public configuration and keep secrets behind your web/API server.
The safe pattern is:
- use modes for environments, such as
development,staging, andproduction - use browser-specific files only when Chrome, Firefox, Edge, or Safari need different values
- expose runtime values with
VITE_orWXT_ - keep signing keys and store credentials in CI secrets, not extension env files
No extension secrets
Do not put private API keys, database URLs, webhook secrets, or service-role tokens in the extension. If the extension needs a privileged action, call your authenticated web/API endpoint.
Choose your environment values
Start with the values the extension needs to know:
VITE_APP_ENV="development"
VITE_SITE_URL="http://localhost:3000"
VITE_DEFAULT_LOCALE="en"
VITE_THEME_MODE="system"
VITE_THEME_COLOR="orange"Use the same names in every environment so your code does not need environment-specific branches.
Create mode-specific files
WXT follows Vite-style env loading and supports mode-specific files:
VITE_APP_ENV="development"
VITE_SITE_URL="http://localhost:3000"
VITE_DEFAULT_LOCALE="en"
VITE_THEME_MODE="system"
VITE_THEME_COLOR="orange"VITE_APP_ENV="staging"
VITE_SITE_URL="https://staging.example.com"
VITE_DEFAULT_LOCALE="en"
VITE_THEME_MODE="system"
VITE_THEME_COLOR="orange"VITE_APP_ENV="production"
VITE_SITE_URL="https://example.com"
VITE_DEFAULT_LOCALE="en"
VITE_THEME_MODE="system"
VITE_THEME_COLOR="orange"Use .local files for machine-specific or sensitive values and keep them ignored.
Add browser-specific overrides only when needed
If a browser needs different values, add the browser to the filename:
VITE_SITE_URL="https://example.com"
VITE_FIREFOX_EXTENSION_ID="extension@example.com"WXT can load files by mode and browser, for example:
.env.production.env.production.local.env.firefox.env.production.firefox.env.production.firefox.local
Keep the shared values in mode files and use browser files only for browser-specific IDs, permissions, or store behavior.
Use env values in extension code
Read public configuration through import.meta.env:
export const config = {
appEnv: import.meta.env.VITE_APP_ENV,
siteUrl: import.meta.env.VITE_SITE_URL,
defaultLocale: import.meta.env.VITE_DEFAULT_LOCALE,
};When using env values inside the manifest, use the function form so WXT can load env files first:
import { defineConfig } from "wxt";
export default defineConfig({
manifest: ({ mode }) => ({
name: mode === "production" ? "Acme" : `Acme (${mode})`,
host_permissions: [`${import.meta.env.VITE_SITE_URL}/*`],
}),
});Add scripts for each target
Use --mode for the environment and -b for the browser target:
{
"scripts": {
"dev:chrome": "wxt -b chrome --mode development",
"dev:firefox": "wxt -b firefox --mode development",
"build:chrome:staging": "wxt build -b chrome --mode staging",
"build:firefox:staging": "wxt build -b firefox --mode staging",
"build:chrome:production": "wxt build -b chrome --mode production",
"build:firefox:production": "wxt build -b firefox --mode production"
}
}Then run:
pnpm --filter extension build:chrome:staging
pnpm --filter extension build:firefox:productionConfigure CI secrets separately
Environment variables that build the bundle are different from secrets used to publish it.
Keep bundle-safe values as normal CI environment variables:
env:
VITE_APP_ENV: production
VITE_SITE_URL: https://example.comKeep store credentials and signing keys as CI secrets:
env:
CHROME_EXTENSION_ID: ${{ secrets.CHROME_EXTENSION_ID }}
CHROME_CLIENT_ID: ${{ secrets.CHROME_CLIENT_ID }}
CHROME_CLIENT_SECRET: ${{ secrets.CHROME_CLIENT_SECRET }}
CHROME_REFRESH_TOKEN: ${{ secrets.CHROME_REFRESH_TOKEN }}Before release, verify:
VITE_SITE_URLpoints to the matching web/API environment- extension origins are allowed by auth and CORS settings
- browser-specific IDs match the store listing
- staging builds use staging API, analytics, and auth settings
- no private values are prefixed with
VITE_orWXT_
Useful references
How is this guide?
Last updated on