skip to content

Launch Options

Everything a launch call lets you set, from headless and channel to args, proxy and a slowed-down run, and why a persistent profile on disk buys extensions by giving up context isolation.

on this pageshow

explore

questions

5

In Playwright, what does the headless option on browserType.launch() control, and what is its default?

level: juniorimportance: must knowfreq 76%

answer

  1. One switch, decided when the browser starts
  2. The quiet default nobody sets
  3. Chromium has two builds, not one
  4. Pair it with slowMo to watch
  5. channel chromium selects new headless mode

basics

~20 s

Playwright's headless launch option decides whether the browser draws a visible window. It defaults to true, so launch() starts an invisible browser; passing headless: false opens a real window you can watch a failing flow in.

solid answer

~40 s

`headless` is an option on `browserType.launch()` (and `launchPersistentContext`) that says whether the browser process draws a window. It defaults to `true` in Playwright 1.63, so `await chromium.launch()` runs invisibly; `{ headless: false }` starts a visible browser. It is a process-level decision fixed at launch: every context and page from that browser inherits it. Headed and headless runs use the same automation protocol, so locators, screenshots and video capture behave identically. One Chromium subtlety matters: with `headless: true` and no `channel`, Playwright runs the separate headless shell build, while `channel: 'chromium'` opts into Chromium's new headless mode, which is the real browser without a window. Headed mode is a debugging tool - pair it with `slowMo` locally and keep it out of CI, where there is usually no display.

code

typescript · 9 lines
typescript
import { chromium } from '@playwright/test';

const browser = await chromium.launch({
  headless: !!process.env.CI,
  slowMo: process.env.CI ? 0 : 250,
});
const page = await browser.newPage();
await page.goto('https://staging.example.com/hotels/search');
await browser.close();

go deeper

for a junior

Remember the default is true and that headless: false is how you watch a run. Know that it belongs in the launch options, not in the test body.

for a middle

Be able to explain that the switch is fixed per browser process and inherited by every context, and that Chromium runs a separate headless shell build unless a channel is set.

for a senior

Show the judgment of keeping headed mode out of committed code, and of treating a headless-only failure as a race in the test rather than as a defect in headless mode.

for a principal

Own the call on whether the suite runs against the headless shell for speed or the real browser through channel chromium for fidelity, and say what evidence would change that choice.

`headless` is one of the options accepted by `browserType.launch()` - the call behind `chromium.launch()`, `firefox.launch()` and `webkit.launch()`. It answers a single question: should this browser process draw a window on screen? In Playwright 1.63 it defaults to `true`, so a bare `await chromium.launch()` gives you an invisible browser, and `await chromium.launch({ headless: false })` gives you one you can watch. ## What the option actually switches - It is a **process-level** decision, fixed the moment the browser starts. Every context and page created from that browser inherits it - you cannot have one page headed and another headless inside the same browser process. - Headed and headless browsers speak the same automation protocol, so `page.goto()`, locators, screenshots and video capture behave the same way in both. - Turning it off is a debugging move: you watch the hotel-booking search page mis-render instead of reading about it in a stack trace. - Because it is a launch option, changing it means starting a new browser. A running test cannot toggle it. ## Chromium quietly runs two different builds This is the part interviewers actually probe. - With `headless: true` and **no** `channel`, Chromium runs the separate **headless shell** build that Playwright ships next to the full browser. It starts fast and is cheap on CI, but it is not the binary your users run. - With `channel: 'chromium'` you opt into Chromium's **new headless mode**: the real browser binary, running without a window. It is more faithful to production behaviour, and it is what lets browser extensions work in a headless run. - The branded channels - `'chrome'`, `'msedge'` and their beta, dev and canary variants - also run a real browser, headed or headless. | Launch options | What actually starts | Typical use | |---|---|---| | `{}` (defaults) | Chromium headless shell | fast, invisible CI runs | | `{ headless: false }` | Full Chromium, visible window | watching a failure locally | | `{ channel: 'chromium' }` | Real Chromium, new headless mode | closest-to-users headless run | Firefox and WebKit have no such split: one binary, with or without a window. ## What headless does not change - It does not change timeouts, auto-waiting or actionability rules. A test that loses a race headless loses it headed too, just less often. - It does not change the viewport. Size comes from context options, not from this switch. - It does not disable screenshots, videos or downloads - all of them work in an invisible browser. - It does not slow anything down for the benefit of your eyes. Headless or not, operations run as fast as the machine allows unless you also pass `slowMo`. ## Watching a run properly 1. Launch headed: `chromium.launch({ headless: false })`. 2. Add `slowMo: 250` so each operation is visible instead of a blur. 3. Reproduce the failing hotel-booking flow - room search, room detail, guest profile. 4. Put both options back behind an environment check before committing; a headed CI job either fails for want of a display or burns machine time no one watches. ## Traps worth naming - **Hardcoding `headless: false` in shared code.** Most CI machines have no display server, so the run dies as an infrastructure failure dressed up as a test failure. - **Expecting `headless: 'new'`.** That spelling belongs to another tool. In Playwright the option is a boolean, and the new headless mode is selected through `channel: 'chromium'`. - **Blaming headless for a flake.** Headless is a rendering decision, not a timing one. If the suite only passes headed, you have found a race in the test, not a bug in headless mode. - **Assuming headless means no browser.** A real browser process starts, consumes real memory, and needs the same system libraries as a headed one. The practical summary: leave the default alone for automated runs, flip it to `false` while you are looking at something with your own eyes, and reach for `channel: 'chromium'` when the headless shell is not a faithful enough stand-in for the browser your guests actually book rooms in.

  • How would you make a single local run headed without editing the code that calls launch()?
    Read the flag from the environment at the launch call - `headless: process.env.HEADED !== '1'` - so one shell variable flips it. The launch options stay committed as headless, and nobody has to remember to revert a debugging edit before pushing.
  • A test passes headed and fails headless on the same machine. Where do you look first?
    At timing, not at rendering. Headed runs are slower, which hides races, so look for an action that fires before the app is ready and replace it with a wait on a real readiness signal. Also check whether the headless shell differs from the real browser by retrying with `channel: 'chromium'`.

saying these in an interview costs you the question

  • Says headless defaults to false and must be enabled
  • Writes headless: 'new' as if it were a string option
  • Claims screenshots and video need a headed browser
  • Commits headless: false so CI matches local runs
  • Treats headless mode as the cause of flaky tests
open as a page

What does Playwright's chromium.launchPersistentContext(userDataDir) buy you, and what does it cost?

level: middleimportance: should knowfreq 37%

basics

~10 s

It launches a browser against a profile directory on disk and returns the single BrowserContext that owns it. You gain state that survives runs plus Chromium extensions; you lose fresh isolation and independent contexts.

open as a page

What does Playwright's slowMo launch option do, and why does it not make a flaky test reliable?

level: middleimportance: should knowfreq 44%

basics

~10 s

slowMo is a launch option that pauses Playwright by the given number of milliseconds between operations so a human can follow the run. It hides races behind extra delay; it never removes them.

open as a page

Your Playwright suite must reach a staging hotel-booking site only through an authenticated HTTP proxy. How do you configure that?

level: seniorimportance: should knowfreq 31%

basics

~20 s

Pass a proxy object to browserType.launch(): server, plus optional username, password and a comma-separated bypass list. Set at launch it covers every context that browser creates, and the credentials should come from environment variables, not source.

open as a page

When is passing custom args or an executablePath to Playwright's launch justified, and what do you take on by doing it?

level: principalimportance: nice to knowfreq 24%

basics

~20 s

Rarely, and only for a capability no supported option provides. Playwright computes a default argument set and supports the browsers it ships; every custom flag or foreign binary is a divergence your team owns, debugs and revisits at each upgrade.

open as a page