Running all tests against Chromium, Firefox, and WebKit on every PR triples CI time without proportional bug coverage, because most failures are not browser-specific. The differences that actually matter across engines are specific: date input rendering, CSS flexbox edge cases, clipboard API permissions, and file upload behavior. This article covers when Chromium-only testing is sufficient, how to configure tiered browser projects in playwright.config.ts, and how to handle browser-specific logic when you need it.

The three browser projects

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

export default defineConfig({
  projects: [
    {
      name: 'chromium',
      use: { ...devices['Desktop Chrome'] },
    },
    {
      name: 'firefox',
      use: { ...devices['Desktop Firefox'] },
    },
    {
      name: 'webkit',
      use: { ...devices['Desktop Safari'] },
    },
  ],
});

Run all browsers:

npx playwright test

Run one browser:

npx playwright test --project=webkit

Chromium, Firefox, and WebKit: what's different

Chromium: The V8 JavaScript engine, Blink rendering. Chrome and Edge use Chromium. ~65% of desktop web traffic in 2026. This is your primary target. Firefox: SpiderMonkey JavaScript engine, Gecko rendering. ~5% of desktop traffic. Different CSS behavior in edge cases, different form element rendering, different handling of some web APIs. WebKit: JavaScriptCore, WebKit rendering. Safari on Mac and iOS. iOS is WebKit-only — all iOS browsers are WebKit under the hood. If your users include iPhone users (they do), WebKit testing is non-optional.

What actually breaks in cross-browser testing

Most web apps work the same in all three engines. The differences that matter:

CSS: gap in flexbox, subgrid, newer @container queries, backdrop-filter, some color functions — all behave slightly differently across engines or are unsupported in older versions. Date/time input: renders very differently across browsers. WebKit and Firefox have their own native date picker UIs. File upload: The file chooser dialog varies. Playwright's setInputFiles() works around this, but filechooser event handling differs. Scrollbar behavior: WebKit (on Mac) hides scrollbars by default. Tests that click at coordinates near scrollbars may behave differently. Clipboard API: navigator.clipboard requires user permission. Works differently in each browser under test automation. element: Full support in all modern browsers, but behavior differences in older engine versions. WebRTC, WebSockets, Service Workers: Generally consistent now, but historically had cross-browser differences.

Cross-browser strategy: tiered testing

Running all tests on all browsers triples your CI time. The practical approach is tiered:

Tier 1: every PR (Chromium only):
  • Full regression suite
  • Feature tests
  • API tests
Tier 2: merge to main (Chromium + Firefox + WebKit):
  • Smoke tests across all browsers
  • Tests in areas with known cross-browser risk (date inputs, file upload, CSS-dependent interactions)
Tier 3: nightly (all browsers, full suite):
  • Complete cross-browser regression

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

const isCIFullRun = process.env.CROSS_BROWSER === 'true';

export default defineConfig({
  projects: [
    // Always run Chromium
    {
      name: 'chromium',
      use: { ...devices['Desktop Chrome'] },
    },

    // Only run Firefox and WebKit in cross-browser mode
    ...(isCIFullRun ? [
      {
        name: 'firefox',
        use: { ...devices['Desktop Firefox'] },
      },
      {
        name: 'webkit',
        use: { ...devices['Desktop Safari'] },
      },
    ] : []),
  ],
});

# .github/workflows/tests.yml
- name: Run cross-browser tests
  if: github.event_name == 'schedule'  # Only on nightly schedule
  run: CROSS_BROWSER=true npx playwright test

Installing browsers

Playwright downloads browser binaries separately. Install all three:

npx playwright install

Install specific browsers:

npx playwright install chromium firefox webkit

In CI, use the Docker image that includes all browsers:

- run: npx playwright install --with-deps

--with-deps also installs system dependencies (fonts, codecs) that Playwright browsers need.

Handling browser-specific behavior in tests

When a test must behave differently per browser:

import { test, expect } from '@playwright/test';

test('date input behavior', async ({ page, browserName }) => {
  await page.goto('/book-appointment');

  if (browserName === 'webkit') {
    // WebKit's native date picker requires different interaction
    await page.getByLabel('Date').fill('2026-06-15');
  } else {
    await page.getByLabel('Date').type('06/15/2026');
  }

  await expect(page.getByLabel('Date')).toHaveValue('2026-06-15');
});

browserName is one of 'chromium', 'firefox', 'webkit'.

Skip a test on a specific browser:

test('uses clipboard API', async ({ page, browserName }) => {
  test.skip(browserName === 'firefox', 'Clipboard API requires permissions in Firefox');
  // ...
});

Real browser vs. emulation

Playwright runs against real browser engines — not simulated environments. WebKit tests run against the actual WebKit engine (the same one that powers Safari), not a Chrome-mimicking Safari simulator.

This matters: cross-browser bugs you find in Playwright's WebKit project will reproduce in real Safari. It's not 100% identical (Playwright uses a slightly newer WebKit build than the shipping Safari), but it's close enough to catch real issues.

→ See also: Cross-Browser Testing Strategies: When and How to Test Multiple Browsers | Mobile Emulation in Playwright: Responsive and Touch Testing | Parallel Execution in Playwright: Workers, Shards, and Sharding for Speed