📝 UPDATE (January 2025):
As of
nextcov@1.1.0withast-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 checkcommand 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:
- Test Runner (Playwright) — orchestrates tests
- Next.js Server (Node.js) — runs server components
- 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
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.tsandnext.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
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
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
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
Note: Prior to v1.1.0, the
checkcommand also scanned for JSX patterns (ternary and logical AND) that V8 couldn't track. As of v1.1.0 withast-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:
- Unit tests (Vitest) — Test utilities, hooks, and pure logic
- Component tests (Vitest Browser Mode) — Test client components in isolation
- E2E tests (Playwright + nextcov) — Test server components, pages, and user flows
-
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
- GitHub: https://github.com/stevez/nextcov
- npm: https://www.npmjs.com/package/nextcov
- Example Project: https://github.com/stevez/nextcov-example
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:
- nextcov - Collecting Test Coverage for Next.js Server Components (this article)
- Why Istanbul Coverage Doesn't Work with Next.js App Router
- V8 Coverage vs Istanbul: Performance and Accuracy
- V8 Coverage Limitations and How to Work Around Them
- How to Merge Vitest Unit, Component, and E2E Test Coverage
- E2E Coverage in Next.js: Dev Mode vs Production Mode



youtube.com/shorts/29ExKnFsFlU?si=...