Max Schmitt

March 15 2024

Next.js: How to Mock Server-Side Requests Using Playwright

When you're writing end-to-end tests for your Next.js application, there might come a time when you want to mock HTTP requests that your app is making.

Maybe your app is interacting with a third-party API and its output isn't predictable. Or maybe you want to test how your app behaves when the server returns an error.

Historically, this has been a bit tricky for Next.js apps where requests can be made from the browser or the server.

My buddy Max (yes, another Max Schmitt – don't get confused!) recently published an experimental package for Playwright that makes it super-easy to mock server-side requests.

Here's an example of how it works:

An Example Page

Let's say we have a simple Next.js project (using the app router) to display my last 3 repositories on GitHub:


export default async function Home() {
const repos = await fetch('', {
// It's important to use cache: 'no-cache', otherwise we can't
// mock the request. See:
cache: 'no-cache',
.then((res) => res.json())
.then(repos => repos.slice(0, 3))
return (
<h1>Latest repositories</h1>
{ any) => {
return <li key={}>{repo.full_name}</li>

In the browser, the page would look something like this:

A website showing 3 GitHub repositories

An Example Playwright Test

If we wanted to test this page, we could write the following test with Playwright:


import { test, expect } from 'playwright'
test('displays latest repos from GitHub', async ({ page }) => {
// Visit the homepage
await page.goto('/')
// Check that the correct title is displayed
await expect(page.locator('h1')).toContainText('Latest repositories')
// Check that we have exactly 3 repos
await expect(page.locator('li')).toHaveCount(3)
await expect(page.locator('li').nth(0)).toContainText('maximilianschmitt/nextjs-playwright-ssr')
await expect(page.locator('li').nth(1)).toContainText('maximilianschmitt/nextjs-contact-form-app')
await expect(page.locator('li').nth(2)).toContainText('maximilianschmitt/nextjs-contact-form-pages')

Now, what happens if I create new repositories? I would need to update the tests!

To avoid this, let's mock the server-side request and return a fixed list of repositories.

Mocking Next.js' Server-Side Requests

We'll use the experimental playwright-ssr package to mock the server-side requests.

Let's update our test file:


// Import 'test' and 'expect' from 'playwright-ssr'
// instead of 'playwright' 👇
import { test, expect } from 'playwright-ssr'
// Get a reference to the webServer fixture 👇
test('displays latest repos from GitHub', async ({ page, webServer }) => {
// Intercept the request to
// and reply with our own list of repos
await webServer.route('*/repos*', async (route) => {
await route.fulfill({
status: 200,
json: [
{ id: 1, full_name: 'maximilianschmitt/hello-world' },
{ id: 2, full_name: 'maximilianschmitt/' },
{ id: 3, full_name: 'maximilianschmitt/cakedesk' },
// Visit the homepage
await page.goto('/')
// ...

Isn't this super easy? I really love how easy it is to describe which request we want to intercept and what response we want to send back.

How to Setup playright-ssr

How do we set this up? It's pretty easy!

First, install playwright-ssr:

npm install playwright-ssr

Then, adjust the playwright.config.ts file like so:


import { defineConfig, devices } from '@playwright/test'
import { WorkerConfigOptions } from 'playwright-ssr'
export default defineConfig<WorkerConfigOptions>({
// ...
projects: [
name: 'chromium',
use: {
...devices['Desktop Chrome'],
webServer: {
command: 'npm',
args: ['run', 'dev'],
url: 'http://localhost:3000',
cwd: __dirname,

And that's it!

Check out the playwright-ssr project on GitHub. You can find the full example from this post on GitHub as well.