Playwright Headless vs Headed: When to Use Each

Understand Playwright headless vs headed mode, when to use each, performance differences, and how to configure them for debugging and CI.

Most teams configure headless vs headed mode in Playwright once and never think about it again, until a test starts failing in CI that works perfectly on their machine.

The confusion comes down to one thing: teams pick a mode without understanding what each one actually does under the hood, and they pay for it with debugging sessions that take twice as long as they should.

This guide breaks down:

  • How headless and headed mode work in Playwright?

  • When to use each?

  • How to configure them in playwright.config.ts and the CLI?

  • The real performance differences you'll see in your pipeline.

TL;DR
  • Headless = no visible window, headed = visible window. Headless is Playwright's default.
  • Headless is faster because it skips painting frames to a display. Large suites see a 30%–50% pipeline-time drop.
  • Since Playwright 1.57, the two modes use different binaries by default: headed runs chrome, headless runs chrome-headless-shell. That is why a test can pass headed and fail headless.
  • The fix for cross-mode rendering gaps is the new headless mode: set channel: 'chromium' so both modes use the same full browser.
  • Workflow that works: headed locally for debugging, headless in CI for speed.

What is the difference between headless and headed mode in Playwright?

Most teams set headless vs headed mode once and never touch it again, until a test fails in CI that passes on their machine.

The cause is almost always the same. A mode gets picked without knowing what it actually does, and the bill arrives later as a debugging session that runs twice as long as it should.

Headless mode runs the browser without opening a visible window. Headed mode opens a full browser window you can see and interact with. That single difference drives speed, resource usage, and how easily you can debug.

In headless mode, the browser engine still loads pages, runs JavaScript, and builds the DOM. It just skips painting pixels to a display. In headed mode, a native window opens and every frame renders on screen.

Here is the side-by-side before we go deeper.

Aspect Headless mode Headed mode
Browser window Not visible Visible on screen
Default in Playwright ✅ (opt out with headless: false)
Speed Faster Slower
CPU / memory usage Lower Higher
Visual debugging Not real-time (use Trace Viewer) Full real-time visibility
Default Chromium binary chrome-headless-shell chrome (full browser)
Best for CI/CD, regression suites, parallel runs Debugging, test development, visual checks
Needs a display server ✅ (or xvfb on Linux)

Playwright defaults to headless because most automated runs do not need a live window. When you need to watch a test interact with the page, switch to headed.

The chrome-headless-shell gotcha: two browser binaries since 1.57

Here is the part most guides skip, and it is the root cause behind "passes headed, fails headless."

Since Playwright 1.57, Playwright runs on Chrome for Testing builds, and the two modes use different binaries by default. Headed mode launches chrome. Headless mode launches chrome-headless-shell, a separate, lighter binary built for automation.

That means headless and headed are not the same browser with one feature toggled off. By default they are two different executables with different rendering paths. Fonts can rasterize differently. GPU and canvas handling can differ. Most of the time it does not matter. When you compare pixel-level screenshots, it does.

The mental model that fixes this: headless vs headed is not a display switch, it is a binary switch. Once you see it that way, "works on my machine" stops being a mystery and becomes a binary-mismatch you can fix in one config line.

One exception worth knowing: on Arm64 Linux, Playwright still uses Chromium rather than Chrome for Testing. (For everything else that shipped recently, see the Playwright 1.60 release guide.)

The fix, when you need the modes to match exactly, is the new headless mode covered below.

Flow diagram of Playwright's browser launch, splitting into a HEADLESS path that uses chrome-headless-shell and a HEADED path that uses full chrome, both running tests through the Chrome DevTools Protocol

How headless and headed mode actually work under the hood

The difference is not which engine runs. It is the rendering path that engine takes.

Playwright talks to browser engines through protocols: the Chrome DevTools Protocol (CDP) for Chromium, and patched internal APIs for Firefox and WebKit. Both modes use the same protocol layer. For a deeper look at that communication layer, our Playwright architecture breakdown traces it layer by layer.

In headless mode:

  • The browser starts without compositing pixels to a window.

  • Layout, JavaScript, network, and DOM all execute normally.

  • Screenshots, videos, and traces are still captured through internal APIs.

In headed mode:

  • A native window opens, and every frame paints to the display.

  • The GPU pipeline handles compositing, animations, and visual effects.

  • That rendering loop is why headed mode consumes more resources.

The takeaway: headless is not a "lite" browser. It is the same engine without a visible output surface, running on a binary tuned for that job.

When should you use headless vs headed mode in Playwright?

Use headless for speed and efficiency. Use headed for visibility and debugging. The right call depends on what you are doing at that moment.

Use headless mode when:

  • Running tests in CI/CD. Runners on GitHub Actions, Jenkins, or GitLab CI have no display server. Headless works out of the box.

  • Executing large regression suites. Fewer resources per test means more parallel workers, which cuts total time.

  • Web scraping or data extraction. No visible window needed.

  • Smoke tests and health checks. Quick validations where visual feedback adds nothing.

Use headed mode when:

  • Debugging a failing test. Watching each action reveals timing issues, wrong selectors, and layout shifts that logs cannot show. --ui mode layers a visual inspector on top, and the Trace Viewer replays a CI failure step by step.

  • Developing new test scripts. Seeing the browser react to your locators in real time speeds up authoring.

  • Visual verification. Animations, hover states, and CSS transitions need a visible window. For structured checks, Playwright visual testing covers the snapshot workflow.

  • Demos and walkthroughs. A live window makes the value obvious to stakeholders.

The workflow that works: develop and debug locally in headed mode, then run headless in CI. Best of both, no compromise.

How to configure headless and headed mode in Playwright

Playwright gives you three ways to switch modes: the config file, a CLI flag, or a direct option to browser.launch().

Compare when to use Playwright headless mode (CI/CD, regression suites, parallel execution, scraping, health checks) versus headed mode (debugging, writing tests, visual verification, demos, animation inspection

1. Using playwright.config.ts

The most common approach. Set headless inside the use block.

playwright.config.ts
import { defineConfig } from '@playwright/test';

export default defineConfig({
  use: {
    headlessfalse// launches a visible browser window
  },
});

Set headless: true (or remove the line, since true is the default) to go back to headless.

2. Using the CLI flag

Override the config for a one-off run without editing files.

terminal
# Run all tests in headed mode
npx playwright test --headed

# Run a specific test file in headed mode
npx playwright test tests/login.spec.ts --headed

# Open Playwright UI mode (headed + visual inspector)
npx playwright test --ui

The --headed flag temporarily overrides whatever is in playwright.config.ts.

3. Using browser.launch() in a script

When using Playwright as a library (outside the test runner), pass the option directly.

script.ts
import { chromium } from 'playwright';

const browser = await chromium.launch({
  headlessfalse// or true
});

Per-project configuration

You can set different modes per project in the same config. Useful when running across Chromium, Firefox, and WebKit.

playwright.config.ts
import { defineConfigdevices } from '@playwright/test';

export default defineConfig({
  projects: [
    {
      name'chromium-headless',
      use: { ...devices['Desktop Chrome'], headlesstrue },
    },
    {
      name'chromium-headed',
      use: { ...devices['Desktop Chrome'], headlessfalse },
    },
  ],
});

The new headless mode (Chromium)

If you want headless runs to use the exact same full browser as headed, set the channel to 'chromium'. This is the cleanest fix for the two-binary gotcha above.

playwright.config.ts
import { defineConfigdevices } from '@playwright/test';
export default defineConfig({
  projects: [
    {
      name'chromium',
      use: {
        ...devices['Desktop Chrome'],
        channel'chromium'// full browser even in headless
      },
    },
  ],
});

What this buys you: The new headless mode removes the rendering differences between headless and headed for Chromium, because both now run the same binary. It is the real Chrome browser, so it is more authentic and supports more features (including browser extensions). That matters most when your tests touch GPU-dependent rendering, font smoothing, or canvas operations. The underlying capability shipped in Chrome 112.

The trade-off: The full browser is heavier than chrome-headless-shell. If your CI only ever runs headless and you do not need cross-mode parity, the default shell is lighter. You can even skip downloading the full browser with npx playwright install --with-deps --only-shell, or skip the shell with --no-shell when you commit to the new headless mode.

Performance: Headless vs headed in Playwright

The short answer: headless is faster and lighter.

Why headless is faster? It skips the rendering pipeline that paints content to your screen:

  • No GPU compositing.

  • No window management overhead.

  • No paint frames sent to a display.

For a single test the gap feels small. Across 500 tests in parallel it compounds. Teams with large suites often see a 30% to 50% reduction in total pipeline time just by confirming headless is on.

Resource comparison

Metric Headless mode Headed mode
Avg. memory per browser context ~80–120 MB ~150–250 MB
CPU during execution Lower (no paint thread) Higher (UI compositing active)
Parallel workers on 4-core CI 4–6 comfortably 2–3 before slowdown
Canvas-heavy operations Up to 10x faster (per Chromium-team benchmarks) Baseline
Screenshot capture Via internal API (fast) Via internal API (fast)

Reproducible benchmark script

Save this as benchmark.js and run node benchmark.js. It measures launch, navigate, and close 10 times in each mode.

benchmark.js
const { chromium } = require('playwright');

async function runBenchmark(headless) {
  const start = performance.now();
  for (let i = 0i < 10i++) {
    const browser = await chromium.launch({ headless });
    const page = await browser.newPage();
    await page.goto('https://playwright.dev');
    await browser.close();
  }

  const end = performance.now();
  console.log(`${headless ? 'Headless' : 'Headed'} took: ${((end - start) / 1000).toFixed(2)}s`);
}

(async () => {
  console.log('Running benchmark...');
  await runBenchmark(true);  // Headless
  await runBenchmark(false); // Headed
})();

Tip: If memory is the bottleneck in CI, switching from headed to headless and raising the worker count is one of the fastest wins available. You remove display overhead and reinvest it in parallelism. For tuning that worker count, see how to optimize Playwright workers.

Headless vs headed mode in CI/CD pipelines

CI is where the choice matters most, and the vast majority of production pipelines run headless.

Card listing the three ways to switch Playwright between headless and headed mode — the config file (use: { headless: false }), the CLI flag (npx playwright test --headed), and a script launch (chromium.launch({ headless: false }))

Why headless dominates CI. Most runners (GitHub Actions, Jenkins agents, GitLab CI) operate with no display server. Running headed there means installing a virtual display like Xvfb on Linux, which adds setup and overhead. Headless works natively. If you are wiring up CI for the first time, the Playwright CI/CD integrations guide walks through GitHub Actions, Jenkins, and GitLab CI.

A minimal CI config for headless execution

.github/workflows/playwright.yml
namePlaywright Tests
on: [pushpull_request]
jobs:
  test:
    runs-onubuntu-latest
    steps:
      - usesactions/checkout@v4
      - usesactions/setup-node@v4
        with:
          node-version20
      - runnpm ci
      - runnpx playwright install --with-deps --only-shell
      - runnpx playwright test

The --only-shell flag skips downloading the full Chromium browser since CI only needs the headless shell. That cuts install time and disk usage. Drop --only-shell (or use --no-shell to skip the shell) when you move CI to the new headless mode.

When headed mode is justified in CI

A few edge cases:

  • Visual regression where pixel parity is critical. Though Playwright's screenshot API works headless too.

  • Record-and-replay debugging. Recording a video in headed mode can expose layout flashing or race conditions that a headless trace may not.

For most teams, headless is the default and the correct one.

Track CI test failures clearly
Centralized dashboards with AI failure insights for Playwright.
Start free CTA Graphic

Common Pitfalls when switching between headless and headed mode

Knowing when to use each mode is half the job. The other half is avoiding the traps that make tests behave differently across modes. Use this matrix as a quick symptom → cause → fix reference.

Symptom Root cause Fix
Passes headed, fails headless Different binaries (chrome vs chrome-headless-shell) rasterize fonts and handle GPU differently Set channel: 'chromium' so both modes share one binary, or pin viewport explicitly
Screenshot diffs only in headless Headless shell uses a different font rasterizer than full chrome Capture baselines and run in the same mode, or move both to the new headless mode
Flaky only in headless Headless runs faster and exposes race conditions the human eye tolerated in headed Use auto-wait and web-first assertions; remove hardcoded timeouts
Canvas / WebGL renders wrong Default shell handles GPU differently from the full browser Switch to channel: 'chromium'
Viewport size differs between modes Headed may inherit OS display settings; headless uses the config viewport Always set viewport in playwright.config.ts
headless: false errors on CI No display server on the runner Keep headless: true in CI and use --headed only locally, or wrap with xvfb-run

Two of the fixes above are worth a code line.

Pin the viewport so neither mode inherits a surprise:

playwright.config.ts
use: {
  viewport: { width1280height720 },
}

Force a display on CI only if you genuinely need headed there:

terminal
xvfb-run npx playwright test

A flaky test passes and fails intermittently with no code change. When it appears after a mode switch, the mode is rarely the real cause. Timing and environment usually are, which our flaky test benchmark found behind more than half of all flaky tests.

Tip: If you are experiencing mode-related flakiness and need real-time insight into failures across your CI runs, TestDino's Playwright observability platform groups recurring Playwright failures, classifies root causes, and tracks whether a failure is new or an old flake across both headless and headed runs, tied back to the PR that triggered it.

Ship tests that stay stable
AI-driven failure grouping and flaky test tracking in CI.
Start free CTA Graphic

Conclusion

Headless vs headed is not about a "better" mode. They solve different problems at different stages.

Headless wins in CI: fewer resources, faster runs, no display required. Headed wins when you need to see what is happening, for debugging, authoring, and visual checks. The one detail that trips teams up is that, since 1.57, the two modes run different binaries by default. When that bites, the new headless mode (channel: 'chromium') puts both on the same browser.

Run headless in your pipelines, headed when something breaks. In Playwright, switching is a single config line or CLI flag away.

FAQs

Does Playwright run in headless mode by default?
Yes. Playwright runs headless by default, with no browser UI on screen. Switch to headed by setting headless: false in playwright.config.ts or passing --headed on the CLI.

What is headless mode in Playwright?
Headless mode runs the browser without a graphical interface. It skips painting pixels to a display, so it is faster and uses fewer resources, which makes it the standard choice for CI/CD.

Why do my tests pass in headed but fail in headless?
Since Playwright 1.57, headed mode uses the chrome binary and headless uses chrome-headless-shell by default. The two can differ in font rendering and GPU handling. Set channel: 'chromium' to run the same full browser in both modes, and always pin an explicit viewport.

When should I use headed mode in Playwright?
Use headed mode when debugging failing tests, verifying UI interactions visually, or writing new scripts. It opens a real window so you can watch every step in real time.

Is headless mode faster than headed mode in Playwright?
Yes. Headless eliminates the overhead of painting a browser window. The gap grows as suite size and parallelism increase, often a 30%–50% pipeline-time reduction on large suites.

How do I switch between headless and headed mode in Playwright?
Set use: { headless: false } in playwright.config.ts for headed, or pass npx playwright test --headed. Revert by setting headless: true or removing the flag.

What is the new headless mode in Chromium?
The new headless mode runs the full Chrome browser instead of the lightweight chrome-headless-shell. Enable it with channel: 'chromium'. It removes rendering differences between headless and headed and is the recommended option when tests depend on GPU, fonts, or canvas.
Pratik Patel

Founder & CEO

Pratik Patel is the founder of TestDino, a Playwright-focused observability and CI optimization platform that gives engineering and QA teams clear visibility into test results, flaky failures, and pipeline health. With 12+ years in QA automation, he has helped startups and enterprises like Scotts Miracle-Gro, Avenue One, and Huma build and scale high-performing QA teams. An active open-source contributor, he regularly writes about modern testing practices, Playwright, and developer productivity.

Get started fast

Step-by-step guides, real-world examples, and proven strategies to maximize your test reporting success