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 testRun one browser:
npx playwright test --project=webkitChromium, 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
- Smoke tests across all browsers
- Tests in areas with known cross-browser risk (date inputs, file upload, CSS-dependent interactions)
- 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 testInstalling browsers
Playwright downloads browser binaries separately. Install all three:
npx playwright installInstall specific browsers:
npx playwright install chromium firefox webkitIn 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