skip to content

What does Playwright's mount fixture actually do in the browser when a component test calls it?

level: middleimportance: should knowfreq 38%

answer

  1. The runner is a driver, not a renderer
  2. Two globals on a page you host
  3. A real navigation happens first
  4. Config says where that page lives

basics

~20 s

It drives a page you host. Playwright puts the browser on the story page found under baseURL, calls that page's window.mount global with the story id and props, and returns a locator for whatever the page rendered.

solid answer

~50 s

Component testing in Playwright 1.63 is a contract between the runner and a page you serve yourself. `baseURL` in the config's `use` block says where that page lives, and the page exposes two globals, `window.mount` and `window.unmount`. When a test calls `await mount('data-grid-default', { rows })`, Playwright makes sure the browser is on that page and then invokes `window.mount` inside it with the story id and the props. Your harness code — built by your own tooling — looks the id up in its story registry and renders the scenario into the document, and `window.unmount` tears it down again. Playwright never renders anything itself; it drives a real browser on a real URL, so real CSS, layout and browser APIs apply. Everything else about a real page applies too — origin, cookies, network — which is why component runs commonly set `serviceWorkers: 'block'`.

code

typescript · 8 lines
typescript
import { defineConfig } from '@playwright/test';

export default defineConfig({
  use: {
    baseURL: 'http://localhost:5173',
    serviceWorkers: 'block',
  },
});

go deeper

for a junior

Hold on to the sequence: navigate to the hosted story page, call window.mount with the id and props, get back a locator. Knowing a page must be served explains most setup failures you will meet.

for a middle

Explain the split of responsibilities. Playwright drives; your page renders through your own bundler. Be able to name baseURL as the wiring and window.mount and window.unmount as the contract the page must satisfy.

for a senior

Diagnose from the symptom. A failure at the first mount points at navigation or the harness bundle, not at the component, and a stale or wrong baseURL reproduces as silently wrong renders rather than as an error.

for a principal

Own the harness as infrastructure. The story page, its build and its baseURL are shared plumbing for every component test in the repo, so decide who maintains it and how its breakages are made obvious rather than mistaken for flaky components.

## Three moving parts, only one of which is Playwright Component testing in Playwright 1.63 is a contract between the test runner and a page that you serve yourself. Three things have to line up: 1. **A page you host** — a small HTML document, built by your own tooling, that knows how to render your stories. 2. **Two globals on that page** — `window.mount` and `window.unmount`, which Playwright calls from the outside. 3. **`baseURL` in the config's `use` block** — how Playwright knows where that page lives. Playwright supplies the browser, the navigation and the `mount` fixture. It does not supply a renderer, a bundler or a story format; the page owns all of that. ## What a mount call does, step by step 1. The test calls `await mount('data-grid-default', { rows })`. 2. Playwright ensures the browser is on the story page resolved from `baseURL`. 3. It invokes `window.mount` inside the page, passing the story id and the props. 4. Your harness code looks that id up in its story registry and renders the scenario into the document. 5. Playwright wraps the rendered root in a `Locator` and the `await` resolves with it. 6. `window.unmount` is the mirror image: it tears the rendered scenario back down. The important consequence of step 3 and 4 is that **your page renders the component, not the runner**. Playwright is a driver here, exactly as it is when it clicks a button on a deployed application. ## baseURL: the one piece of wiring you must get right `baseURL` is an ordinary Playwright option set under `use`, and in a component run it points at wherever the story page is served — typically a local dev server started for the run. - Point it at the wrong port and the mount fails on a page that has no `window.mount` at all. - Point it at a stale server and every story renders yesterday's build, with nothing in the test to hint at it. - It is the same `baseURL` option other Playwright features consume, so a project that overrides it for another purpose can break the component run silently. ## Consequences of it being a real page A story page is a real document on a real origin in a real browser, and everything true of a real page is true here. | Because it is a real page… | You get | You also inherit | |---|---|---| | Real engine and layout | true CSS, fonts, media queries | slower than an in-process render | | Real origin | cookies, storage, same-origin rules | origin-scoped state to keep clean | | Real network stack | assets and fetches behave normally | service workers can intercept | That last row is the one teams meet first. A story page built by a modern dev server may register a service worker, which then serves cached assets to every later mount; component runs commonly set `serviceWorkers: 'block'` in the same `use` block so nothing installs during the run. ## Where the model breaks, and how it announces itself - **No `window.mount` on the page** — the id resolves nowhere, and the symptom is a failure at the very first mount in the file rather than at an assertion. - **Unknown story id** — the page's registry has no entry, so nothing renders; the locator resolves to nothing and the first assertion times out. - **Harness renders into a detached node** — the component exists in memory but never enters the document, so it is invisible to every locator. - **Page-level errors before mount** — a throwing bundle means the globals were never defined, which again surfaces as a mount failure and not as a component bug. ## Why the model is shaped this way Putting rendering in a page you own is what keeps Playwright framework-agnostic: the runner has to know only how to navigate, call two globals and build a locator, so anything that can render into a document can be tested. It also means the component under test runs through the same build pipeline as production — the same bundler, the same CSS, the same polyfills — so what the test sees is what a browser would really produce, rather than a stripped-down approximation assembled by the test runner.

  • Why does Playwright call globals on the page instead of rendering the component itself?
    It keeps the runner framework-agnostic. Playwright only has to navigate, call `window.mount` and build a locator, so any stack that can render into a document works. It also means the component goes through your real build pipeline, so the test sees production CSS and output.
  • What is the first thing to check when every mount in a file fails immediately?
    Whether the browser actually reached your story page. A wrong or stale `baseURL`, a dev server that is not running, or a bundle that threw before defining `window.mount` all produce a failure at the mount call rather than at an assertion, and they look identical from the test.
  • Why do component runs commonly set serviceWorkers to 'block'?
    Because the story page is a real page on a real origin. If the harness registers a service worker it can serve cached assets to later mounts and sit between the page and the network, so the run stops reflecting the build you just made. Blocking registration removes that variable.

saying these in an interview costs you the question

  • Believes Playwright renders the component in the runner process
  • Thinks no server or hosted page is involved at all
  • Cannot say what baseURL points at in a component run
  • Assumes the story page is generated automatically by Playwright
  • Treats the story page as exempt from normal browser behaviour