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

# Preview command to launch built extensions

> Launch an already-built extension for production-like manual testing without recompiling. Load unpacked output and run the browser launcher flow.

Launch an already-built extension output for production-like manual testing.

`preview` does not compile your project. It loads an existing unpacked extension root and runs the browser launcher flow.

## When to use `preview`

* Running existing build output without rebuilding.
* Comparing packaged behavior across browser targets quickly.
* Debugging runtime issues tied to production artifacts rather than dev/watch mode.

## Preview command capabilities

| Capability | What it gives you |
| - | - |
| Build-output validation | Test real production artifacts without rebuilding |
| Browser-target checks | Run compiled output against selected browser targets |
| Runner control | Launch or skip browser runner based on workflow needs |
| Fast manual QA | Verify packaging-ready behavior quickly before release |

> `preview` is run-only. It prefers `dist/<browser>` when that output exists. You can also point it at another unpacked extension folder that already contains a `manifest.json`.

## Usage

<CodeGroup>
  ```bash npm theme={null}
  extension preview [project-path] [options]
  ```

  ```bash pnpm theme={null}
  extension preview [project-path] [options]
  ```

  ```bash yarn theme={null}
  extension preview [project-path] [options]
  ```

  ```bash bun theme={null}
  extension preview [project-path] [options]
  ```

  ```bash deno theme={null}
  extension preview [project-path] [options]
  ```
</CodeGroup>

If you omit the path, Extension.js uses the current working folder.

## How `preview` chooses what to run

`preview` checks these locations in order:

1. `--output-path <dir>` when you pass it. It wins over everything else.
2. `dist/<browser>` for the selected browser target.
3. The provided project path or current working folder.

The folder needs to contain an unpacked extension with a `manifest.json`. It does not matter whether a build ran in the same command.

## Arguments and flags

| Flag | Alias | What it does | Default |
| - | - | - | - |
| `[path]` | - | Preview built extension from a project path. | `process.cwd()` |
| `--browser <browser>` | - | Browser/engine target (`chromium`, `chrome`, `edge`, `firefox`, etc.). | `commands.preview.browser`, else `chromium` |
| `--profile <path\|boolean>` | - | Browser profile path or boolean profile mode. | fresh profile |
| `--chromium-binary <path>` | - | [Custom Chromium-family binary path](/docs/browsers/running-other-browsers). | system default |
| `--gecko-binary <path>` | `--firefox-binary` | [Custom Gecko-family binary path](/docs/browsers/running-other-browsers). | system default |
| `--starting-url <url>` | - | Starting URL in launched browser. | unset |
| `--no-open` | - | Launch the browser, but do not open a tab for your extension. The browser still starts and loads it. See [stop the browser from launching](#stop-the-browser-from-launching). | a tab opens on launch |
| `--no-browser` | - | Stop the browser from launching. See [stop the browser from launching](#stop-the-browser-from-launching). | browser launch enabled |
| `--port <port>` | - | Runner/devtools port when runner is enabled. Use `0` for OS-assigned port. | `8080` |
| `--extensions <list>` | - | Comma-separated companion extensions or store URLs. | unset |
| `--output-path <dir>` | - | Existing unpacked extension directory to run. | `dist/<browser>` when available |
| `--output <pretty\|json>` | - | Result format. `json` prints a schema-1 envelope on stdout. | `pretty` |
| `--debug` | - | Enable maintainer diagnostics. | disabled |

`--author` and `--author-mode` are hidden, deprecated aliases for `--debug`.

## Browser support

`preview` has no Safari path. Passing `--browser safari` (or `webkit-based`) exits with `E_COMMAND_UNSUPPORTED_FOR_TARGET`. Safari is a supported browser, but this command cannot launch it. The reason is measured. Safari's WebDriver route does load an unpacked folder, and the background even runs, but Safari grants the extension zero host origins. Content scripts never inject, and no API call can grant the access afterwards. Use [`dev`](/docs/commands/dev) or [`build`](/docs/commands/build) for Safari targets.

## Remote URLs and light mode

When the path argument is a remote `http(s)` URL, `preview` sets `EXTJS_LIGHT=1` automatically. This runs the launch in light mode for downloaded extensions. Set `EXTJS_LIGHT` yourself beforehand to override this behavior.

Under `--output json`, the download progress lines go to stderr, so stdout holds only the result frame.

## Stop the browser from launching

Two flags sound alike and do different things:

| You want | Flag | What happens |
| - | - | - |
| No browser at all | `--no-browser` | No browser process starts. `preview` writes its readiness metadata. |
| A browser, but no tab opened for your extension | `--no-open` | The browser starts and loads your extension. No tab is opened for it. |

```bash theme={null}
# Stop the browser launch.
extension preview --no-browser

# Launch the browser without opening a tab for the extension.
extension preview --no-open
```

`--no-browser` also has a config form, `commands.preview.noBrowser: true`, and an environment form, `EXTENSION_CLI_NO_BROWSER=1`. `dev`, `start`, and `preview` all accept both flags.

## Automation metadata

`preview` writes readiness metadata to:

* `dist/extension-js/<browser>/ready.json`

For `--no-browser` flows, this provides deterministic command state:

* `starting` while command initializes
* `ready` when run-only validation is complete
* `error` when required output is missing or startup fails
* `runId` and `startedAt` for session correlation in scripts/agents

`preview` does not provide a `--wait` gate flag. For `preview` automation, consume `ready.json` directly.

### Machine output with `--output json`

`--output json` prints one schema-1 envelope on stdout:

* A successful run prints a `status: "ready"` frame. Its `value` carries the list of previewed browsers, plus `projectPath` when you passed a path argument.
* A project path that does not exist prints `ok: false` with `status: "usage"` and `error.code: "E_PROJECT_NOT_FOUND"`. A folder with no manifest prints the same status with `E_MANIFEST_NOT_FOUND`. Neither frame carries a hint.
* When the project exists but the output path holds no unpacked extension, the frame is `ok: false` with `status: "not-found"` and `error.code: "E_PREVIEW_NO_DIST"`. Its hint says to run `extension build` first.
* A bad `--chromium-binary` or `--gecko-binary` pin prints `ok: false` with `status: "usage"` and `error.code: "E_BROWSER_BINARY_INVALID"`. That covers a path that does not exist, a file that is not executable, and a binary that does not answer its version probe within 10 seconds. The command ends and leaves nothing running.
* A remote URL that gives no usable archive prints `ok: false` with `status: "failed"`. The code is `E_REMOTE_ZIP_INVALID` for a reply that is not a ZIP archive, a damaged one, or one with an entry outside its folder, `E_REMOTE_FETCH_TIMEOUT` for a timeout, and `E_REMOTE_DOWNLOAD` for a refused connection or an HTTP error.
* A browser that cannot start prints `ok: false` with `status: "failed"`. The code is `E_BROWSER_LAUNCH` when the binary is executable but the system cannot start it or Firefox exits before its debugger answers, and `E_BROWSER_CONNECT` when Firefox runs but its debugger never answers. A config file that fails to load prints the same status with `E_CONFIG_LOAD`.
* Other failures print `ok: false` with `status: "failed"` before the process exits `1`.

## Logging flags

These flags are experimental and may change between minor releases.

| Flag | What it does | Default |
| - | - | - |
| `--logs <off\|error\|warn\|info\|debug\|trace\|all>` | Minimum log level. | `off` |
| `--log-context <list\|all>` | Context filter (`background`, `content`, `page`, `popup`, `options`, `sidebar`, `devtools`, `newtab`, `history`, `bookmarks`). | `all` |
| `--log-format <pretty\|json\|ndjson>` | Logger output format. | `pretty` |
| `--no-log-timestamps` | Disable timestamps in pretty mode. | timestamps enabled |
| `--no-log-color` | Disable color in pretty mode. | color enabled |
| `--log-url <pattern>` | Filter log events by URL substring/regex. | unset |
| `--log-tab <id>` | Filter log events by tab ID. | unset |

## Shared global options

Also supports [Global flags](/docs/workflows/global-flags).

## Examples

### Previewing a local extension

<Frame>
  <iframe className="w-full aspect-video rounded-xl" src="https://www.youtube-nocookie.com/embed/-awORl1WfFQ?rel=0" title="Extension.js: extension preview" loading="lazy" allow="encrypted-media; picture-in-picture; fullscreen" allowFullScreen />
</Frame>

<CodeGroup>
  ```bash npm theme={null}
  extension preview ./my-extension
  ```

  ```bash pnpm theme={null}
  extension preview ./my-extension
  ```

  ```bash yarn theme={null}
  extension preview ./my-extension
  ```

  ```bash bun theme={null}
  extension preview ./my-extension
  ```

  ```bash deno theme={null}
  extension preview ./my-extension
  ```
</CodeGroup>

### Previewing in Edge and Chrome

<CodeGroup>
  ```bash npm theme={null}
  extension preview ./my-extension --browser=edge,chrome
  ```

  ```bash pnpm theme={null}
  extension preview ./my-extension --browser=edge,chrome
  ```

  ```bash yarn theme={null}
  extension preview ./my-extension --browser=edge,chrome
  ```

  ```bash bun theme={null}
  extension preview ./my-extension --browser=edge,chrome
  ```

  ```bash deno theme={null}
  extension preview ./my-extension --browser=edge,chrome
  ```
</CodeGroup>

### Preview without launching the browser

<CodeGroup>
  ```bash npm theme={null}
  extension preview ./my-extension --no-browser
  ```

  ```bash pnpm theme={null}
  extension preview ./my-extension --no-browser
  ```

  ```bash yarn theme={null}
  extension preview ./my-extension --no-browser
  ```

  ```bash bun theme={null}
  extension preview ./my-extension --no-browser
  ```

  ```bash deno theme={null}
  extension preview ./my-extension --no-browser
  ```
</CodeGroup>

## Behavior notes

* `preview` is run-only and never compiles the project.
* `preview` prefers existing build output (`dist/<browser>`) but can fall back to another unpacked extension root.
* `preview` does not run watch mode or hot module replacement (HMR).
* For scripts/agents, rely on `ready.json` and avoid parsing terminal output.

## Best practices

* Run `build` before `preview` when testing a fresh production artifact.
* Pass the project path argument when your unpacked extension lives outside the default project output.
* Use `--browser` to verify behavior across targets before packaging.

## Next steps

* Build and launch in one step with [`start`](/docs/commands/start).
* Generate production artifacts with [`build`](/docs/commands/build).
* Configure shared defaults in [`extension.config.js`](/docs/features/extension-configuration).
* Review configuration env loading behavior in [Environment variables](/docs/features/environment-variables#how-it-works).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.