nextcov - Collecting Test Coverage for Next.js Server Components
Steve Zhang

Steve Zhang @stevez

About: I am passionate about programming, I love to share tips and tricks of coding

Joined:
Feb 8, 2025

nextcov - Collecting Test Coverage for Next.js Server Components

Publish Date: Jan 1
1 1

📝 UPDATE (January 2025):

As of nextcov@1.1.0 with ast-v8-to-istanbul@0.3.10:

  • JSX branch coverage now works! Ternary (? :) and logical AND (&&) patterns in JSX now have proper branch coverage tracking
  • The nextcov check command now only checks project configuration (not JSX patterns)
  • These were not V8 limitations but bugs in the V8-to-Istanbul conversion that are now fixed

If you've ever tried to get test coverage for React Server Components, you know the frustration. Unit testing frameworks like Jest and Vitest can't render server components — they require the full Next.js runtime. So you write E2E tests with Playwright, but then you're left wondering: "What's actually being covered?"

nextcov solves this problem by collecting V8 coverage from both client and server during Playwright E2E tests, then merging it with your unit and component test coverage for a complete picture.

The Testing Landscape in 2025

Modern Next.js applications have three layers that need testing:

Layer What to Test Best Tool
Utilities & Hooks Pure functions, custom hooks Vitest (unit tests)
Client Components React components with user interaction Vitest Browser Mode (component tests)
Server Components RSC, server actions, full pages Playwright (E2E tests)

Vitest Browser Mode — Great for Client Components

Vitest's Browser Mode is excellent for component testing. It runs tests in a real browser, giving you accurate DOM behavior and event handling. Combined with @vitest/coverage-v8, you get proper coverage for your client components.

But there's a limitation: Vitest Browser Mode only works for client components. Server components require the full Next.js runtime — they can't be rendered in isolation.

The Server Component Gap

React Server Components (RSC) are notoriously difficult to test:

  • They run only on the server — Can't be rendered in jsdom or even a real browser
  • Async components fetch data directly — Mocking becomes complex and unreliable
  • Tight coupling with Next.js runtime — Server actions, cookies, headers require the full framework

The practical solution? Test server components through E2E tests with Playwright, where they run in their natural environment.

But this creates a coverage gap. You have coverage from unit tests, coverage from component tests... but nothing from E2E tests.

nextcov Fills the Gap

nextcov completes the coverage picture by collecting V8 coverage during Playwright E2E tests:

Test Type Coverage Tool What It Covers
Unit (Vitest) @vitest/coverage-v8 Utilities, hooks, logic
Component (Vitest Browser) @vitest/coverage-v8 Client components
E2E (Playwright) nextcov Server components, pages, user flows

And the best part? nextcov merges all three into a single unified report.

Real Results

In a production Next.js application:

Coverage Type Lines
Unit Tests ~45%
Component Tests ~35%
E2E Tests (nextcov) ~46%
Merged ~88%

Each test type contributes coverage that others can't reach. The merged report shows exactly what's tested and what's not.

How It Works

Unlike traditional coverage tools that work within a single Node.js process, nextcov coordinates coverage collection across three separate environments:

  1. Test Runner (Playwright) — orchestrates tests
  2. Next.js Server (Node.js) — runs server components
  3. Browser (Chromium) — runs client components

For the browser, nextcov uses Playwright's CDP integration to collect V8 coverage. For the server, it uses Node.js's built-in NODE_V8_COVERAGE environment variable, triggered via Chrome DevTools Protocol.

This multi-process approach is what makes nextcov unique compared to tools like c8 or @vitest/coverage-v8, which only work within a single process.

Quick Start

# Install
npm install nextcov --save-dev

# Interactive setup
npx nextcov init
Enter fullscreen mode Exit fullscreen mode

The init command creates all the necessary configuration files:

  • e2e/global-setup.ts — Initialize coverage collection
  • e2e/global-teardown.ts — Finalize and generate reports
  • e2e/fixtures/test-fixtures.ts — Playwright fixture for per-test coverage
  • Updates to playwright.config.ts and next.config.ts

Then run your E2E tests with coverage:

# Build with source maps
E2E_MODE=true npm run build

# Start server with coverage enabled and run tests
NODE_V8_COVERAGE=.v8-coverage NODE_OPTIONS='--inspect=9230' npm start &
npx playwright test
Enter fullscreen mode Exit fullscreen mode

Merging All Coverage

The real power comes from merging E2E coverage with your unit and component test coverage:

# Merge all coverage sources
npx nextcov merge coverage/unit coverage/component coverage/e2e -o coverage/merged
Enter fullscreen mode Exit fullscreen mode

This produces a unified HTML report showing the complete coverage picture — what's covered by unit tests, component tests, E2E tests, and what's still missing.

Checking Project Configuration

nextcov includes a check command to validate your project configuration for common issues that affect V8 coverage:

npx nextcov check
Enter fullscreen mode Exit fullscreen mode

The check command detects:

  • Missing or outdated browserslist — Can cause phantom branches from transpiled ?. and ?? operators
  • Babel configuration — May transpile modern syntax and break V8 coverage mapping
  • Source maps not enabled — Required for mapping bundled code back to source
  • Missing dependencies — Playwright or Vitest not installed

Example output:

Project Configuration:
────────────────────────────────────────────────────────────
  ⚠ Missing browserslist - ?? and ?. operators may be transpiled, causing phantom branches
  ⚠ Source maps not enabled in next.config - add productionBrowserSourceMaps: true
    next.config.ts
Enter fullscreen mode Exit fullscreen mode

Note: Prior to v1.1.0, the check command also scanned for JSX patterns (ternary and logical AND) that V8 couldn't track. As of v1.1.0 with ast-v8-to-istanbul@0.3.10, these patterns now have proper branch coverage, so JSX scanning has been removed.

The Complete Testing Strategy

Here's the recommended approach for Next.js applications:

  1. Unit tests (Vitest) — Test utilities, hooks, and pure logic
  2. Component tests (Vitest Browser Mode) — Test client components in isolation
  3. E2E tests (Playwright + nextcov) — Test server components, pages, and user flows
  4. Merge coverage — Get the complete picture with nextcov merge

This gives you fast feedback from unit and component tests during development, comprehensive E2E tests for critical user flows, and a unified coverage report that shows exactly what's tested.

Features

  • Next.js + Vite support — Works with both frameworks
  • Client + Server coverage — Browser and Node.js in one report
  • Dev mode support — Works with next dev (no production build required)
  • Auto-detection — Automatically detects dev vs production mode
  • Source map support — Maps bundled code back to original TypeScript/JSX
  • Multi-source merge — Combine unit, component, and E2E coverage
  • Config validation — Check project configuration with nextcov check
  • Multiple reporters — HTML, LCOV, JSON, text-summary

Try It Out

If you're building a Next.js application and want complete test coverage visibility across unit, component, and E2E tests, give nextcov a try.


This is part 1 of a series on test coverage for modern React applications:

  1. nextcov - Collecting Test Coverage for Next.js Server Components (this article)
  2. Why Istanbul Coverage Doesn't Work with Next.js App Router
  3. V8 Coverage vs Istanbul: Performance and Accuracy
  4. V8 Coverage Limitations and How to Work Around Them
  5. How to Merge Vitest Unit, Component, and E2E Test Coverage
  6. E2E Coverage in Next.js: Dev Mode vs Production Mode

Comments 1 total

Add comment