Published on Friday, July 28, 2023, last updated

JavaScript Code Coverage: Istanbul vs Jest vs Vitest vs c8

Authors

For most JavaScript projects, the best code coverage tool is the one built into your test runner: Vitest (V8 coverage by default) or Jest (jest --coverage, Istanbul instrumentation by default). Use Istanbul's nyc or c8 for other runners such as Mocha or the Node.js test runner, and a service like Codecov to track coverage on every pull request.

Table of Contents

1. JavaScript code coverage

1.1 What is code coverage?

jest code coverage tool screenshot

Code coverage measures how much of your source code runs while your tests run. It is reported as a percentage, usually split into four metrics:

  • Statements: how many statements were executed.
  • Branches: how many branches of each if, switch or ternary were taken.
  • Functions: how many functions were called.
  • Lines: how many lines were executed.

A low number points to code that no test exercises, which is where bugs are most likely to slip into production.

1.2 Code coverage limitations

Code coverage tells you which code ran, not whether your tests checked the right result. A test that calls a function without asserting anything still counts as coverage. High coverage can therefore give a false sense of security, and 100% coverage does not mean every input or state of your application is tested. Use coverage to find untested code, and combine it with good assertions, code review and other testing practices.

“High code coverage can give a false sense of security if the tests are poorly written or if they don't adequately check for correct outcomes. ”

— ...doesn't account for different paths through the code that might occur under varying conditions....it should be complemented with other testing strategies and quality assurance practices to ensure comprehensive and effective testing.

1.3 Two ways to measure JavaScript coverage

JavaScript coverage tools work in one of two ways:

  • Instrumentation (Istanbul). The tool rewrites your code before it runs to add counters on every statement, branch and function. It works in any JavaScript runtime, but adds overhead.
  • Native V8 coverage. Node.js, Deno and Chromium browsers run on the V8 engine, which can record coverage itself. Tools like c8, Vitest's default provider and Jest's v8 provider read that data and convert it to the same report formats as Istanbul. It is usually faster, but only works on V8-based runtimes (not Firefox or Bun).

Both produce the same report formats (text, html, lcov, json), so you can switch without changing your CI or reporting service.

Axolo User Experiences

2480+ developers online

Axolo Logo

2. JavaScript code coverage tools compared

ToolTypeHow it measuresUse it withPrice
Istanbul (nyc)Coverage CLIInstrumentationMocha, AVA, tap, any Node.js testFree, open source
JestTest runner with coverageIstanbul via Babel (default) or V8Jest testsFree, open source
VitestTest runner with coverageV8 (default) or IstanbulVitest testsFree, open source
c8Coverage CLINative V8Any Node.js processFree, open source
Node.js test runnerBuilt into Node.jsNative V8 (experimental)node --testFree
CodecovCoverage reporting (SaaS)Collects reports from the tools aboveAny CI, GitHub, GitLab, BitbucketFree for 1 user and open source, paid from $5/user/month

2.1 Criteria for selecting a code coverage tool

  • Your test runner. If you use Jest or Vitest, start with their built-in coverage. A separate tool is only needed for other runners.
  • Your runtime. Native V8 coverage needs Node.js, Deno or a Chromium browser. Istanbul instrumentation works everywhere.
  • Accuracy and speed. V8 coverage is usually faster; Istanbul is the long-standing reference for accurate branch reports.
  • Reports. Check that the tool outputs lcov if you upload coverage to a service or show it in your editor.
  • CI/CD integration. Coverage thresholds that fail the build, and coverage comments on pull requests, keep coverage from slowly dropping.

3. Detailed review of each tool

3.1 Istanbul (nyc)

istanbul code coverage tool screenshot

3.1.1 Overview of Istanbul

Istanbul is the original JavaScript code coverage library, and nyc is its command-line client. It instruments your JavaScript and TypeScript code with counters to track how well your tests exercise your codebase, and it works with most Node.js test frameworks, such as Mocha, AVA and tap. nyc is still maintained (version 18 was released in February 2026), and Istanbul's instrumentation and report formats are also used under the hood by Jest and by Vitest's istanbul provider.

3.1.2 Features of Istanbul

  • Source-mapped coverage for Babel and TypeScript projects.
  • Coverage of applications that spawn subprocesses.
  • Coverage thresholds that fail the run when check-coverage is on and coverage drops below them.
  • Caching of instrumented files to speed up repeated runs.
  • Many report formats: text, html, lcov, json, cobertura and more.

3.1.3 Pros and cons of Istanbul

👍 Pros:

  • Battle-tested for more than a decade, with accurate branch coverage.
  • Works on any JavaScript runtime, because coverage comes from instrumentation.
  • Highly configurable through .nycrc files or the nyc key in package.json.

👎 Cons:

  • Instrumentation makes tests slower and uses more memory than native V8 coverage.
  • The configuration can be complex for beginners.

3.1.4 How to use Istanbul for code coverage

Add nyc as a dev dependency with npm i -D nyc (or yarn add -D nyc), then wrap your test command:

{
  "scripts": {
    "test": "mocha",
    "coverage": "nyc npm run test"
  }
}

Run npm run coverage to see a text summary. Add --reporter=html --reporter=lcov to generate an HTML report and an lcov.info file. For advanced usage, see the Istanbul documentation.

3.2 Jest

jest code coverage tool screenshot

3.2.1 Overview of Jest

Jest is a JavaScript testing framework created at Meta and now maintained under the OpenJS Foundation. It works out of the box for most JavaScript projects and includes test running, assertions, mocking, snapshots and code coverage in a single package. The current major version is Jest 30.

3.2.2 How Jest measures code coverage

Jest has a coverageProvider option with two values:

  • babel (default): Jest instruments your code with Istanbul through Babel. This is the "Jest Istanbul" combination many teams search for: you get Istanbul's reports without installing nyc.
  • v8: Jest uses native V8 coverage, which avoids the instrumentation step.

By default, Jest writes clover, json, lcov and text reports to a coverage folder, and you can enforce minimums with coverageThreshold.

3.2.3 Pros and cons of Jest

👍 Pros:

  • Coverage is built in: no extra package to install.
  • Rich API for mocking, spying and snapshot testing.
  • Very large community and ecosystem.

👎 Cons:

  • Often slower than Vitest, especially with the default Babel instrumentation.
  • Native ES modules support still needs extra configuration.

3.2.4 How to use Jest for code coverage

Install Jest as a dev dependency:

npm install --save-dev jest

Add a script in your package.json:

{
  "scripts": {
    "test": "jest",
    "coverage": "jest --coverage"
  }
}

Run npm run coverage. Jest prints a summary table and writes the full report to the coverage folder. To switch to V8 coverage and fail the run under 80% of lines, add this to your Jest config:

{
  "coverageProvider": "v8",
  "coverageThreshold": { "global": { "lines": 80 } }
}

For more details, see the Jest configuration documentation.

3.3 Vitest

3.3.1 Overview of Vitest

Vitest is a test runner built on Vite, with a Jest-compatible API. It is widely used in Vite-based projects (Vue, Svelte, React and others). Coverage is built in and supports two providers:

  • v8 (default, @vitest/coverage-v8): native V8 coverage. Since Vitest 3.2, it remaps coverage using the code's syntax tree, so its reports match Istanbul's while keeping V8's speed.
  • istanbul (@vitest/coverage-istanbul): instrumentation, for runtimes that are not based on V8, such as Firefox or Bun.

3.3.2 Pros and cons of Vitest

👍 Pros:

  • Fast, with V8 coverage as accurate as Istanbul.
  • Native ES modules and TypeScript support, no Babel needed.
  • Jest-compatible API, which makes migration easier.

👎 Cons:

  • Best suited to projects that already use Vite, or that can adopt it.
  • By default, the report only shows files imported by your tests; you need coverage.include to see untested files.

3.3.3 How to use Vitest for code coverage

Install the provider and run Vitest with --coverage:

npm i -D vitest @vitest/coverage-v8
npx vitest run --coverage

To include every source file in the report, even those that no test imports:

import { defineConfig } from "vitest/config"

export default defineConfig({
  test: {
    coverage: {
      provider: "v8", // or 'istanbul'
      include: ["src/**/*.{ts,tsx}"],
    },
  },
})

See the Vitest coverage guide for all options.

3.4 c8

c8 collects coverage with Node.js's native V8 coverage and outputs Istanbul-compatible reports. Because it wraps any Node.js process, it works with any test runner, including Mocha and the Node.js test runner, without instrumenting your code. It is actively maintained (version 12 was released in July 2026).

npm i -D c8
npx c8 --reporter=text --reporter=lcov mocha
npx c8 node --test

Choose c8 if you run tests on Node.js with a runner that has no coverage of its own, and want something faster and simpler than nyc.

3.5 Node.js built-in test runner coverage

Node.js has its own test runner (node --test) and can collect coverage without any package:

node --test --experimental-test-coverage

It prints a coverage summary after the tests, and can write an lcov file with --test-reporter=lcov --test-reporter-destination=lcov.info. You can ignore lines with /* node:coverage ignore next */ comments. The feature is still marked experimental in the Node.js documentation, so pin your Node.js version in CI if you rely on it.

3.6 Codecov

codecov code coverage tool screenshot

3.6.1 Overview of Codecov

Codecov (owned by Sentry) does not measure coverage itself. It collects the reports produced by the tools above in your CI, then shows coverage on every pull request: a comment with the coverage change, status checks, and patch coverage (how much of the new code is tested). It works with GitHub, GitLab and Bitbucket.

3.6.2 Codecov pricing

Codecov's pricing page lists four plans:

  • Developer: free, 1 user, unlimited public repositories.
  • Team: 5peruserpermonth(5 per user per month (4 billed annually), up to 10 users.
  • Pro: $12 per user per month.
  • Enterprise: custom pricing.

3.6.3 How to use Codecov

Sign in with your Git provider and add your repository. Generate an lcov report with your coverage tool, then upload it from your CI with the Codecov CLI or the official GitHub Action. See the Codecov documentation for the setup of each CI.

3.7 What about JS Coverage (jscoverage)?

The jscoverage package was one of the first JavaScript coverage tools, but its last release dates from 2015. It does not support modern JavaScript or TypeScript, so use one of the tools above instead.

4. Which JavaScript code coverage tool should you choose?

  • You use Vitest: use its built-in coverage with the default v8 provider.
  • You use Jest: run jest --coverage. Keep the default Istanbul instrumentation, or try coverageProvider: "v8" if coverage slows down your tests.
  • You use Mocha, AVA or tap: use nyc for the most mature option, or c8 for faster native coverage.
  • You use the Node.js test runner: try --experimental-test-coverage, or use c8 if you need a stable tool.
  • You want coverage on every pull request: add Codecov (or a similar service) on top of any of these tools.

Coverage is most useful when reviewers see it next to the code they are reviewing. For more ways to make reviews faster and more thorough, read our code review best practices and tools.

Axolo is a Slack app to help techteams review pull request seamlessly

Streamline your code reviewsJump directly to sign up for free.