How to Monitor a Next.js Application with Synthetic Monitoring
Set up production monitoring for your Next.js app. Learn to write Playwright tests for SSR pages, API Routes, middleware, and authentication with next-auth.
Next.js applications have unique monitoring needs. Server-side rendering (SSR), API Routes, middleware, and dynamic content all create potential failure points that traditional uptime monitors miss. Synthetic monitoring with Playwright catches these issues by simulating real user behavior.
Why Next.js Apps Need Synthetic Monitoring
Next.js combines frontend and backend in a single deployment, creating several surfaces that can break independently:
| Next.js Feature | What Can Break | Impact |
|---|---|---|
| Server Components | Rendering errors, data fetch failures | Blank pages or error boundaries |
| API Routes | Handler errors, database timeouts | Broken forms, failed actions |
| Middleware | Redirect loops, auth gating failures | Users locked out or seeing wrong content |
| ISR/Static | Stale content, revalidation failures | Outdated information |
| Edge Runtime | Cold start timeouts, region issues | Slow or unavailable pages |
Monitoring Server-Rendered Pages
Next.js Server Components fetch data during rendering. If the data source fails, the entire page can break.
import { test, expect } from "@playwright/test";
test("homepage renders with dynamic content", async ({ page }) => {
await page.goto("https://your-nextjs-app.com");
// Verify SSR content loaded (not just the shell)
await expect(page.getByRole("heading", { level: 1 })).toBeVisible();
// Check that dynamic data rendered (not loading skeleton)
await expect(page.getByTestId("featured-items")).not.toHaveText("Loading...");
await expect(page.getByTestId("featured-items")).toBeVisible();
});Monitoring API Routes
Test your Next.js API Routes directly to catch backend issues separately from frontend rendering:
test("API route returns valid data", async ({ request }) => {
const response = await request.get(
"https://your-nextjs-app.com/api/products"
);
expect(response.status()).toBe(200);
const data = await response.json();
expect(data.products).toBeDefined();
expect(data.products.length).toBeGreaterThan(0);
});Server Actions
If you use Next.js Server Actions, monitor the forms they power:
test("contact form submits successfully", async ({ page }) => {
await page.goto("https://your-nextjs-app.com/contact");
await page.getByLabel("Name").fill("Synthetic Test");
await page.getByLabel("Email").fill("test@example.com");
await page.getByLabel("Message").fill("Automated monitoring test");
await page.getByRole("button", { name: "Send" }).click();
// Server Action should redirect or show success
await expect(page.getByText("Message sent")).toBeVisible();
});Monitoring Authentication (next-auth)
Authentication is one of the most critical flows to monitor. If users can't log in, nothing else matters.
test("user can log in with credentials", async ({ page }) => {
await page.goto("https://your-nextjs-app.com/login");
await page.getByLabel("Email").fill(process.env.TEST_USER_EMAIL!);
await page.getByLabel("Password").fill(process.env.TEST_USER_PASSWORD!);
await page.getByRole("button", { name: "Sign In" }).click();
// Verify redirect to authenticated page
await expect(page).toHaveURL(/.*dashboard/);
await expect(page.getByText("Welcome")).toBeVisible();
});[!TIP] Create a dedicated test account for monitoring. Disable MFA, exclude from analytics, and give it minimal permissions. Store credentials in environment variables.
Monitoring Middleware and Redirects
Next.js middleware can redirect users, rewrite URLs, or modify headers. Monitor that these work correctly:
test("unauthenticated users are redirected to login", async ({ page }) => {
// Visit a protected page without logging in
const response = await page.goto(
"https://your-nextjs-app.com/dashboard"
);
// Should redirect to login
await expect(page).toHaveURL(/.*login/);
});
test("locale redirect works", async ({ page }) => {
await page.goto("https://your-nextjs-app.com/", {
extraHTTPHeaders: { "Accept-Language": "fr-FR" },
});
// Should redirect to French locale
await expect(page).toHaveURL(/.*\/fr\/.*/);
});Handling Dynamic and Client-Side Content
Next.js apps often use client-side data fetching for interactive sections:
test("dashboard loads real-time data", async ({ page }) => {
// Login first
await page.goto("https://your-nextjs-app.com/login");
await page.getByLabel("Email").fill(process.env.TEST_USER_EMAIL!);
await page.getByLabel("Password").fill(process.env.TEST_USER_PASSWORD!);
await page.getByRole("button", { name: "Sign In" }).click();
// Wait for client-side data to load
await page.waitForLoadState("networkidle");
// Verify dynamic content appeared
await expect(page.getByTestId("stats-panel")).toBeVisible();
await expect(page.getByTestId("recent-activity")).not.toHaveText("No data");
});Recommended Check Configuration
| Flow | Frequency | Priority |
|---|---|---|
| Homepage load | Every 5 min | High |
| Authentication (login) | Every 5 min | Critical |
| Core feature (dashboard) | Every 10 min | High |
| API Routes health | Every 5 min | High |
| Contact/signup forms | Every 15 min | Medium |
| Marketing pages | Every 30 min | Low |
Next Steps
- Writing Playwright Tests — Playwright fundamentals
- Modern SPA Monitoring — Patterns for client-side apps
- CI/CD Integration — Verify after every deploy
- Environment Variables — Secure credential management
Monitoring Modern SPAs (React, Vue, Next.js)
Synthetic monitoring for single-page applications (SPAs) requires a different approach. Learn how to handle client-side routing, hydrations, and state transitions with Playwright.
How to Monitor Password Reset Flow (Complete SaaS Guide)
Learn how to monitor a password reset flow with synthetic checks, from forgot-password requests to token validation and successful login completion.