playwright.config is the single source of truth for how tests run: timeouts, retries, reporters, the shared use options (baseURL, trace, viewport), and projects — named run configurations that let one suite execute across browsers, devices, and setup dependencies.
Projects are the key concept. Each project is a named configuration with its own use options and can depend on other projects. This is how you run the same tests on Desktop Chrome, Firefox, WebKit and emulated mobile devices (devices['Pixel 7'], devices['iPhone 14']) from one command, and how you wire an auth 'setup' project as a dependency so login runs first. Shared options live under top-level use (baseURL so tests navigate with relative paths, trace/screenshot policies, default viewport, locale, timezone). Because config is code, you can branch on process.env.CI for retries and reporters. Good config is a large part of a suite's quality: it encodes cross-browser coverage, artifact capture, and environment targeting in one reviewable file.
One command must cover desktop Chrome/Firefox/Safari plus emulated iPhone and Pixel, all starting authenticated. The config defines a 'setup' project for login and five browser/device projects that depend on it and load storageState — so npx playwright test runs the whole matrix, authenticated, with per-project traces.
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
use: { baseURL: process.env.BASE_URL ?? 'https://www.qapractice.com', trace: 'on-first-retry' },
reporter: process.env.CI ? [['blob'], ['github']] : [['html']],
projects: [
{ name: 'setup', testMatch: /auth\.setup\.ts/ },
{ name: 'chromium', use: { ...devices['Desktop Chrome'], storageState: 'auth/user.json' }, dependencies: ['setup'] },
{ name: 'firefox', use: { ...devices['Desktop Firefox'], storageState: 'auth/user.json' }, dependencies: ['setup'] },
{ name: 'webkit', use: { ...devices['Desktop Safari'], storageState: 'auth/user.json' }, dependencies: ['setup'] },
{ name: 'mobile-chrome', use: { ...devices['Pixel 7'], storageState: 'auth/user.json' }, dependencies: ['setup'] },
],
});Scenario: The team wants the same suite to run against local (localhost:3000), staging and production, across three browsers, without duplicating test files. How do you structure playwright.config to do this cleanly?