# How to Build the Extension for Firefox Versus Chrome Using the Vite React Boilerplate

> Learn to build browser extensions for Firefox and Chrome with the Vite React Boilerplate. Effortlessly switch between targets using environment flags. Get started today!

- Repository: [JongHak Seo/chrome-extension-boilerplate-react-vite](https://github.com/jonghakseo/chrome-extension-boilerplate-react-vite)
- Tags: how-to-guide
- Published: 2026-03-05

---

**The boilerplate uses a single source tree controlled by the `CLI_CEB_FIREFOX` environment flag; set it to `false` (default) for Chrome or `true` for Firefox using the npm scripts `pnpm dev`/`pnpm build` for Chrome and `pnpm dev:firefox`/`pnpm build:firefox` for Firefox.**

The `jonghakseo/chrome-extension-boilerplate-react-vite` repository provides a modern development environment for browser extensions using React and Vite. When you need to build the extension for Firefox versus Chrome, the project avoids code duplication by using environment-driven configuration to target each browser from the same codebase.

## The Environment Flag That Controls the Build

The build system relies on the **`CLI_CEB_FIREFOX`** environment variable to toggle between Chrome and Firefox targets. When this flag is `false` or undefined, the build pipeline generates a Manifest V3 bundle optimized for Chrome. When set to `true`, the configuration activates Firefox-specific polyfills and manifest adjustments.

The flag is managed by the helper script located at **[`bash-scripts/set_global_env.sh`](https://github.com/jonghakseo/chrome-extension-boilerplate-react-vite/blob/main/bash-scripts/set_global_env.sh)**. This script validates the input and writes the appropriate values to the `.env` file, ensuring that subsequent Vite processes inherit the correct configuration consistently.

## Development Workflow

For active development with hot module replacement, the boilerplate provides distinct npm scripts that automatically configure the environment before starting the Vite dev server.

To develop for Chrome:

```bash
pnpm dev

```

This command sets `CLI_CEB_DEV=true` and keeps `CLI_CEB_FIREFOX=false`, starting the watch mode with Chrome-compatible output in the `dist` folder.

To develop for Firefox:

```bash
pnpm dev:firefox

```

This script invokes `pnpm set-global-env` to write `CLI_CEB_FIREFOX=true` to the environment before launching Vite. The build pipeline then generates a Firefox-compatible bundle in the same `dist` directory, allowing you to test in Firefox while maintaining the same source files.

## Production Builds

When preparing the extension for distribution, use the production build commands defined in [`package.json`](https://github.com/jonghakseo/chrome-extension-boilerplate-react-vite/blob/main/package.json).

For Chrome production builds:

```bash
pnpm build

```

This generates a production-optimized bundle in the `dist` folder containing a Chrome Manifest V3 [`manifest.json`](https://github.com/jonghakseo/chrome-extension-boilerplate-react-vite/blob/main/manifest.json) and all associated assets.

For Firefox production builds:

```bash
pnpm build:firefox

```

This command first sets `CLI_CEB_FIREFOX=true` via the environment helper script, then executes the production build. The output in `dist` contains a Firefox-compatible manifest and any necessary polyfills, ready for submission to the Firefox Add-ons store.

## How the Manifest Adapts to Each Browser

The manifest generation logic resides in **[`chrome-extension/manifest.ts`](https://github.com/jonghakseo/chrome-extension-boilerplate-react-vite/blob/main/chrome-extension/manifest.ts)**. This TypeScript file imports the **`IS_FIREFOX`** constant from **[`packages/env/lib/const.ts`](https://github.com/jonghakseo/chrome-extension-boilerplate-react-vite/blob/main/packages/env/lib/const.ts)**, which reflects the value of `process.env.CLI_CEB_FIREFOX`.

Based on the `IS_FIREFOX` boolean, the manifest generator conditionally includes browser-specific fields. For example, Firefox may require different background script configurations or specific permissions that differ from Chrome's Manifest V3 implementation. This approach keeps the manifest logic DRY while allowing precise control over each browser's requirements.

## Loading the Built Extension

After building, you must load the extension differently in each browser.

**Chrome:**

1. Navigate to `chrome://extensions`
2. Enable **Developer mode** using the toggle in the top-right corner
3. Click **Load unpacked**
4. Select the `dist` folder from your project directory

**Firefox:**

1. Navigate to `about:debugging#/runtime/this-firefox`
2. Click **Load Temporary Add-on...**
3. Select the [`manifest.json`](https://github.com/jonghakseo/chrome-extension-boilerplate-react-vite/blob/main/manifest.json) file inside the `dist` folder

These steps are documented in the repository's README under the respective installation sections for Chrome and Firefox.

## Summary

- The boilerplate uses a single codebase for both browsers, controlled by the **`CLI_CEB_FIREFOX`** environment flag.
- Use **`pnpm dev`** for Chrome development and **`pnpm dev:firefox`** for Firefox development.
- Use **`pnpm build`** for Chrome production and **`pnpm build:firefox`** for Firefox production.
- The **[`bash-scripts/set_global_env.sh`](https://github.com/jonghakseo/chrome-extension-boilerplate-react-vite/blob/main/bash-scripts/set_global_env.sh)** script manages environment variables by writing to `.env`.
- Manifest adaptation occurs in **[`chrome-extension/manifest.ts`](https://github.com/jonghakseo/chrome-extension-boilerplate-react-vite/blob/main/chrome-extension/manifest.ts)** based on the **`IS_FIREFOX`** constant from **[`packages/env/lib/const.ts`](https://github.com/jonghakseo/chrome-extension-boilerplate-react-vite/blob/main/packages/env/lib/const.ts)**.
- Load the extension in Chrome via `chrome://extensions` and in Firefox via `about:debugging`.

## Frequently Asked Questions

### How do I switch between Chrome and Firefox builds without manually editing files?

The npm scripts `pnpm dev:firefox` and `pnpm build:firefox` automatically invoke `pnpm set-global-env`, which runs [`bash-scripts/set_global_env.sh`](https://github.com/jonghakseo/chrome-extension-boilerplate-react-vite/blob/main/bash-scripts/set_global_env.sh) to write the correct `CLI_CEB_FIREFOX` value to your `.env` file. You do not need to manually edit environment variables.

### Can I build for both browsers simultaneously?

The boilerplate is designed to build for one target at a time. The `dist` folder is shared between targets, so running `pnpm build` after `pnpm build:firefox` will overwrite the Firefox bundle with the Chrome version. To maintain both builds, you should copy the `dist` folder to a separate location between builds or modify the output directory in your Vite configuration.

### What specific manifest differences does the boilerplate handle automatically?

The [`chrome-extension/manifest.ts`](https://github.com/jonghakseo/chrome-extension-boilerplate-react-vite/blob/main/chrome-extension/manifest.ts) file uses the `IS_FIREFOX` boolean (from [`packages/env/lib/const.ts`](https://github.com/jonghakseo/chrome-extension-boilerplate-react-vite/blob/main/packages/env/lib/const.ts)) to conditionally include browser-specific fields. This typically includes differences in background script configuration, specific permission keys, and Firefox-specific manifest keys that differ from Chrome's Manifest V3 implementation. The exact differences depend on the current state of the repository's manifest generator.

### Where does the built extension output go?

Regardless of whether you target Chrome or Firefox, the build output always goes to the `dist` folder at the project root. The contents of this folder will contain the appropriate [`manifest.json`](https://github.com/jonghakseo/chrome-extension-boilerplate-react-vite/blob/main/manifest.json) and assets for whichever browser you last built for. This consistent output location simplifies the loading process in both `chrome://extensions` and `about:debugging`.