# Live preview

> The emulated browser on the right of code.extension.dev runs every surface of your extension and reloads only what an edit changed.

Canonical: https://docs.extension.dev/code/preview
This page as Markdown: https://docs.extension.dev/code/preview.md
Section: Code
Siblings: [AI generation](https://docs.extension.dev/code.md) · [Terminal](https://docs.extension.dev/code/terminal.md) · [Chat mode](https://docs.extension.dev/code/chat-mode.md) · [Starters and templates](https://docs.extension.dev/code/starters.md) · [Select to edit](https://docs.extension.dev/code/select-to-edit.md) · [The Code tab](https://docs.extension.dev/code/code-tab.md) · [Build console](https://docs.extension.dev/code/build-console.md) · [Checkpoints](https://docs.extension.dev/code/checkpoints.md) · [Drafts](https://docs.extension.dev/code/drafts.md) · [Firefox engine](https://docs.extension.dev/code/firefox-engine.md) · [Download and run locally](https://docs.extension.dev/code/download-and-run.md) · [Share links](https://docs.extension.dev/code/share.md) · [Publish and import](https://docs.extension.dev/code/publish.md) · [Generation allowance](https://docs.extension.dev/code/allowance.md) · [Settings and sign-in](https://docs.extension.dev/code/settings.md)
Map of every page: https://docs.extension.dev/llms.txt · Whole corpus: https://docs.extension.dev/llms-full.txt

---

# Live preview

The live preview is the emulated browser in the right column, where your
extension runs as you edit it. Reach for it to open the popup, drive a
content script on a sample page, and watch the background worker, without
installing anything.

## Quick start

```text
preview-open popup
preview-errors
```

Type those in the terminal after a build. The popup opens in the preview, and
`preview-errors` prints the build status, the surfaces that are ready, and
the reload class that ran last.


## How it works

### What runs in the preview

The preview reads `manifest.json` and runs each surface it declares. Content
scripts run on a sample host page and on six fixture pages the preview
serves itself, so nothing is fetched from the internet.

| Surface | Manifest key |
| --- | --- |
| Popup | `action.default_popup` |
| Options page | `options_ui.page` |
| Side panel | `side_panel.default_path` |
| DevTools panel | `devtools_page` |
| New tab page | `chrome_url_overrides.newtab` |
| History and bookmarks overrides | `chrome_url_overrides.history` and `chrome_url_overrides.bookmarks` |
| Content scripts | `content_scripts[].js` |
| Background worker | `background.service_worker` |

### What an edit reloads

Every write compiles on its own after a short wait, and the preview reloads
only what the build changed. It reads which artifacts changed bytes and
chooses one of four classes. `preview-errors` names the class on a `Reload:`
line:

```text
Build: success
Surfaces ready: popup
Reload: worker (service_worker (background.js))
```

- **frame.** A surface changed: its page, or a script or stylesheet it
  loads. Only that surface remounts. An open popup stays open and is reborn
  in place.
- **content.** A content script changed. The sample host page reloads and the
  new script is injected into the fresh document.
- **worker.** The background service worker changed. The worker restarts, the
  popup closes and its ports disconnect, and the sample host page reloads so
  the content scripts the new worker talks to are its own.
- **full.** `manifest.json` or `_locales/` changed, or a file only the
  manifest names, such as an icon. Everything reloads on a fresh session.

A worker restart and a full reload clear the action badge and title, the way
Chrome does on a reload.

### Two chosen differences from Chrome

Chrome has no worker-only restart. A reload tears the whole extension down,
closes every extension page including an open popup, and leaves the old
content script orphaned in a tab that is still open. The preview keeps the
popup open on a frame reload, so you can watch it change as you edit it, and
reloads the sample host page on a worker restart. Both differences are
written down in the emulator's parity record, measured on Chrome 143.

### The wait before a build

The workbench waits 150 ms after a lone terminal write and 500 ms after a
burst. A second write landing inside the open wait makes it a burst and
resets the wait to 500 ms, so a chain of writes lands as one cycle. `build`
skips the wait and compiles now.

### Browser profile

Each preview starts on a fresh browser profile, which is what a local dev run
does by default. Anything the extension writes to storage is gone when the
preview closes. **Settings** has a **Browser profile** card with one toggle:
**Keep this project's browser profile** keeps storage between runs, and
**Discard it when the preview closes** returns to the default. A kept profile
also offers **Start clean**. The change takes effect the next time the
preview starts.

### Where the preview is faithful

The preview is faithful within these bounds, and the preview's own
limitations panel lists them. Confirm each one against a real unpacked load.

| Capability | In the preview |
| --- | --- |
| Offscreen documents | Tracked, but their scripts do not run |
| Camera and screen capture | No synthetic stream for `getUserMedia` or `getDisplayMedia` |
| Web auth | `launchWebAuthFlow` captures same-origin auto-redirects only |
| Native messaging | Answered by an echo host; real hosts report as not found |
| Request filtering | `declarativeNetRequest` and `webRequest` emulated for declared hosts only |
| JS dialogs | `alert`, `confirm` and `prompt` appear as your own browser's dialogs |

## Reference

| Limit | Value |
| --- | --- |
| Wait after a lone write | 150 ms |
| Wait after a burst of writes | 500 ms |
| Reload classes | frame, content, worker, full |
| Fixture pages for content scripts | 6 |
| Browser profile default | Discarded when the preview closes |

## When it fails

- **The popup closed while you edited the background worker.** That is the
  worker class. The worker restarts and the popup closes the way Chrome
  closes it. Run `preview-open popup` again.
- **Storage is empty every time the preview restarts.** The profile is
  discarded by default. Switch to **Keep this project's browser profile** in
  **Settings**.
- **A capture, auth or native messaging flow never completes.** It is one of
  the scoped capabilities above. Prove it on a real browser with
  [Download and run locally](/code/download-and-run).
- **An icon change did not show.** A file only the manifest names triggers a
  full reload. Wait for the `Reload: full` line, then look again.

## Next

- [Terminal](/code/terminal) for the `preview-*` commands that read and drive
  the preview
- [Build console](/code/build-console) for the build status under the
  preview
- [Firefox engine](/code/firefox-engine) for a real Gecko beside the emulator
- [Select to edit](/code/select-to-edit) for pointing a prompt at an element
