- Published on Friday, July 28, 2023, last updated
JavaScript Code Coverage: Istanbul vs Jest vs Vitest vs c8
- Authors

- Name
- Sydney Cohen
- @chnsydney
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?
- 1.2 Code coverage limitations
- 1.3 Two ways to measure JavaScript coverage
- 2. JavaScript code coverage tools compared
- 2.1 Criteria for selecting a code coverage tool
- 3. Detailed review of each tool
- 3.1 Istanbul (nyc)
- 3.2 Jest
- 3.3 Vitest
- 3.4 c8
- 3.5 Node.js built-in test runner coverage
- 3.6 Codecov
- 3.7 What about JS Coverage (jscoverage)?
- 4. Which JavaScript code coverage tool should you choose?
1. JavaScript code coverage
1.1 What is code coverage?

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,switchor 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
v8provider 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

2. JavaScript code coverage tools compared
| Tool | Type | How it measures | Use it with | Price |
|---|---|---|---|---|
| Istanbul (nyc) | Coverage CLI | Instrumentation | Mocha, AVA, tap, any Node.js test | Free, open source |
| Jest | Test runner with coverage | Istanbul via Babel (default) or V8 | Jest tests | Free, open source |
| Vitest | Test runner with coverage | V8 (default) or Istanbul | Vitest tests | Free, open source |
| c8 | Coverage CLI | Native V8 | Any Node.js process | Free, open source |
| Node.js test runner | Built into Node.js | Native V8 (experimental) | node --test | Free |
| Codecov | Coverage reporting (SaaS) | Collects reports from the tools above | Any CI, GitHub, GitLab, Bitbucket | Free 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
lcovif 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)

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-coverageis on and coverage drops below them. - Caching of instrumented files to speed up repeated runs.
- Many report formats:
text,html,lcov,json,coberturaand 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
.nycrcfiles or thenyckey inpackage.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

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.includeto 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

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: 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
v8provider. - You use Jest: run
jest --coverage. Keep the default Istanbul instrumentation, or trycoverageProvider: "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.