
Next.js Setting up Playwright
Given how often this series has pointed toward "use E2E testing" as the answer for whatever a given unit-testing tool doesn't yet support — async Server Components, chiefly — it's worth having Playwright's own setup covered in real depth, since it's frequently the tool actually doing that E2E work in a modern App Router test suite. Its defining feature relative to the alternatives: genuine cross-browser automation — Chromium, Firefox, and WebKit — driven from one consistent API, rather than committing to just one rendering engine.
The fastest path
npx create-next-app@latest --example with-playwright with-playwright-app
Setting it up manually
npm init playwright
This is a genuinely interactive setup — it walks you through a series of prompts (which browsers to install, whether to add a GitHub Actions workflow, TypeScript or JavaScript) and produces a playwright.config.ts reflecting your choices. Playwright's own installation guide is worth a look directly if you want a fuller walkthrough of every prompt's implications beyond what's summarized here.
Writing your first test
The same two-page navigation example used throughout this testing series:
// app/page.tsx
import Link from "next/link";
export default function Page() {
return (
<div>
<h1>Home</h1>
<Link href="/about">About</Link>
</div>
);
}
// app/about/page.tsx
import Link from "next/link";
export default function Page() {
return (
<div>
<h1>About</h1>
<Link href="/">Home</Link>
</div>
);
}
// tests/example.spec.ts
import { test, expect } from "@playwright/test";
test("should navigate to the about page", async ({ page }) => {
await page.goto("http://localhost:3000/");
await page.click("text=About");
await expect(page).toHaveURL("http://localhost:3000/about");
await expect(page.locator("h1")).toContainText("About");
});
Setting baseURL: 'http://localhost:3000' in playwright.config.ts lets you write the shorter page.goto('/') throughout your suite instead of repeating the full URL in every test — a small convenience worth adopting immediately rather than retrofitting later once you have dozens of tests already written the longer way.
Running against a real build
The same principle from the Cypress article applies here with equal force: Playwright genuinely drives real browsers against your running app, and testing against a production build more closely matches what actual users experience than testing against the dev server does.
npm run build
npm run start
Then, in a separate terminal:
npx playwright test
If juggling two terminals manually feels like unnecessary friction — and it especially does in CI, where there's no human available to coordinate the timing — Playwright's own webServer config option lets Playwright itself start the dev server and wait until it's genuinely ready before running tests, collapsing the two-terminal dance into one command.
What "cross-browser, one API" actually buys you
This is worth dwelling on rather than treating as a marketing checkbox, because it's the single biggest practical differentiator between Playwright and the other tools in this series. The exact same test file above runs, unmodified, against Chromium, Firefox, and WebKit — Playwright handles the considerable complexity of automating each engine's own quirks internally, so your test code stays engine-agnostic. WebKit specifically is worth calling attention to, since it's the closest automatable approximation available to Safari's actual rendering behavior — a class of bug that only manifests in Safari (a CSS quirk, a JS API difference) is one that Chromium-only E2E testing would never catch at all, and WebKit coverage through Playwright is a genuinely practical way to close exactly that gap without needing an actual Mac and actual Safari in your CI pipeline.
Running in CI
Playwright runs headless by default — no separate flag needed the way Cypress requires cypress run versus cypress open. The one setup step CI environments specifically need that local development machines usually don't: installing the browser binaries and their OS-level dependencies explicitly.
npx playwright install-deps
Skip this in a fresh CI container image, and tests fail with missing-system-dependency errors that have nothing to do with your actual test code or application logic — worth recognizing immediately as an environment-setup issue rather than assuming your tests themselves are broken.
Key Takeaways
| Task | Command |
|---|---|
| Quickstart | npx create-next-app@latest --example with-playwright |
| Manual install | npm init playwright (interactive setup) |
| Test file location | tests/*.spec.ts |
| Run against production | npm run build && npm run start, then npx playwright test |
| Let Playwright manage the server | webServer option in playwright.config.ts |
| CI browser dependencies | npx playwright install-deps |
| Biggest differentiator | Genuine Chromium + Firefox + WebKit coverage from one API |
Playwright's real value in a Next.js test suite isn't just "another E2E tool" — it's specifically the WebKit coverage that closes a real, otherwise-hard-to-test gap (genuine Safari-adjacent behavior, without needing actual Safari), combined with being the recommended fallback wherever this series' other testing tools hit the async-Server-Component ceiling. If your app has any meaningful Safari user base, Playwright is worth choosing for that reason alone, independent of anything else it offers.


