Playwright is the best E2E testing tool for Next.js applications in 2026. It tests Chromium, Firefox, and WebKit with a single API, handles complex scenarios Cypress cannot (cross-origin iframes, multiple browser tabs, true mobile emulation), and runs faster on CI. The hard part is not setting it up. The hard part is writing tests that are resilient to UI changes.
What Playwright Provides
Playwright is a browser automation library maintained by Microsoft. It gives you programmatic control over real browser instances. You navigate to URLs, interact with elements, and assert that the page state matches your expectations.
Multi-browser. A single Playwright test can run against Chromium (Google Chrome, Microsoft Edge), Firefox, and WebKit (Safari). Real browser diversity is essential for catching browser-specific bugs before users do.
Multi-tab and cross-origin. Playwright can control multiple browser tabs simultaneously and navigate across different origins. This is useful for testing OAuth flows, popup windows, and multi-step flows that open new tabs.
True mobile emulation. Playwright's mobile emulation sets the viewport, device pixel ratio, user agent, and touch events to match real devices. It emulates touch events correctly, which matters for mobile-specific interactions.
Network interception. Playwright can intercept HTTP requests and return mocked responses. This lets you test loading states, error states, and edge cases without a real backend.
Setup in a Next.js Project
Install Playwright:
npm init playwright@latest
This generates a playwright.config.ts and an example test. Configure it for Next.js:
The locator hierarchy (most resilient to least resilient):
getByRole - matches elements by their ARIA role and accessible name
getByLabel - matches form inputs by their label
getByText - matches elements by their text content
getByTestId - matches elements by a data-testid attribute you add
CSS selectors (page.locator(".my-class")) - most brittle, avoid when possible
Network Interception: Mocking API Responses
Playwright can intercept network requests and return mocked responses. This is essential for testing loading states, error states, and edge cases where your backend cannot reliably produce a specific response:
test("shows error state when API fails", async ({ page }) => {
await page.route("/api/projects", (route) => {
route.fulfill({
status: 500,
contentType: "application/json",
body: JSON.stringify({ error: "Internal server error" }),
});
});
await page.goto("/projects");
await expect(page.getByText("Something went wrong")).toBeVisible();
await expect(page.getByRole("button", { name: "Retry" })).toBeVisible();
});
You can also use page.route to delay responses and test loading states:
The hardest part of E2E testing is writing tests that do not break every time you make a small UI change. Brittle tests are worse than no tests, because they create noise that teaches developers to ignore test failures.
Brittle tests have these characteristics:
They select elements by CSS class names that change when you refactor styles
They assert on exact pixel positions or element counts that change legitimately
They hard-code waits (page.waitForTimeout(2000)) instead of waiting for specific conditions
They test implementation details rather than user-observable behavior
Resilient tests:
Use semantic locators (getByRole, getByLabel)
Assert on user-visible outcomes ("the user sees a success message") not internal state
Use Playwright's built-in auto-waiting (assertions retry until the condition is met or times out)
Test critical user flows, not every UI detail
A good E2E test reads like a user journey: "The user goes to the sign-in page, enters their credentials, clicks Sign In, and arrives at the dashboard."
Component Testing vs E2E Testing
Use Playwright's E2E tests for critical user flows: sign-in, sign-up, creating a record, completing a payment, core CRUD operations. These tests give you confidence that the most important paths through your application work correctly.
Use component-level tests (Vitest with Testing Library, or Storybook play functions) for component behavior: form validation, state transitions, edge cases in a single component's logic. Component tests are faster and more targeted than E2E tests.
Do not use E2E tests for everything. An E2E test suite that covers every UI state is expensive to run and expensive to maintain. Aim for 20-40 E2E tests covering the critical paths, and rely on component tests for the rest.
Pristren builds AI-powered software for teams. Zlyqor is our all-in-one workspace - chat, projects, time tracking, AI meeting summaries, and invoicing - in one tool. Try it free.
Practical deep-dives on LLMs, developer tools, and AI engineering. No filler. Unsubscribe any time.
// written byFIG. AUTH-01
530
Mahmudul Haque Qudrati
CEO & ML Engineer
CEO and ML Engineer at Pristren. Builds AI-powered software for teams and writes about machine learning, LLMs, developer tools, and practical AI applications.
Open Code Review – An AI-powered code review CLI tool: A Practical Overview
Open Code Review is an open-source CLI tool from Alibaba that uses AI to review code changes. It runs locally, supports multiple LLMs, and costs about $0.01 per review. Here's a practical breakdown.