Skip to main content

Cloudsoft Zone Build, deploy and scale with practical guides to PHP, Node.js, React, DevOps and AI automation. Explore the guides

Cloudsoft Zone
DevelopmentComparison

Vitest 5 vs Jest 30: which JavaScript test runner to choose

Vitest 5 shipped in September 2026 and Jest 30 is on 30.5. Compare ESM, TypeScript, mocking, speed and migration effort, then pick a runner with confidence.

Cloudsoft Zone

7 min read

Row of clean glass test tubes standing in a polished metal rack
Photograph: J.N. Eskra / CC BY-SA 4.0, via Wikimedia Commons
In this article

For a new TypeScript or ESM project in October 2026, choose Vitest: it runs ES modules and TypeScript natively through Vite, and NestJS 12 now generates Vitest for ESM projects. Stay on Jest 30 if you have a large, stable CommonJS suite, rely on Jest-specific features such as automatic __mocks__ loading, or simply have no pain to fix. Both are actively maintained, and the test code itself looks almost identical.

This comparison uses the current releases, Vitest 5.0.3 and Jest 30.5.2 on npm, and the official release notes and migration guides for each.

The short comparison

Vitest 5Jest 30
Released5.0 announced 3 September 202630.0 announced 4 June 2025; 30.5.2 current
Runtime requirementsNode.js 22.12.0 or later and Vite 6.4.0 or laterNode.js 18.x or later; TypeScript 5.4 or later for its type definitions
ES modulesNative, through Vite's module pipelineExperimental; needs Node's --experimental-vm-modules flag
TypeScriptTransformed out of the box, no extra configVia a transformer such as babel-jest or ts-jest
Globals (describe, expect)Imported from vitest, or enabled with globals: trueAvailable by default
Mock APIvi.fn, vi.mock, vi.spyOnjest.fn, jest.mock, jest.spyOn
Browser testingBrowser Mode with real browsers, now with Trace Viewjsdom environment (jsdom 26)
Config filevitest.config.ts or your existing vite.config.tsjest.config.js or jest.config.ts

What changed in Vitest 5

The Vitest team describes performance as the main focus of 5.0. The release adds shared Vite servers for inline projects, makes the on-disk module cache a stable fsModuleCache option, adds a vitest doctor command that suggests faster configuration, and bundles Vitest's own dependencies to reduce install size. New APIs include vi.when for per-argument mock behaviour, a --repeats flag for hunting flaky tests, and fake timers that now mock Temporal by default.

Three breaking changes are worth knowing before you upgrade or adopt:

  • clearMocks now defaults to true, so mock call history is cleared before every test. Set clearMocks: false to keep the old behaviour.
  • Unawaited async assertions now fail. A forgotten await before expect(...).resolves or .rejects used to log a warning; in Vitest 5 it fails the test.
  • Test name patterns changed. Suite and test names are now joined with ' > ' rather than a single space, so a -t filter that matched across the boundary Jest-style may stop matching.

What Jest 30 brought

Jest 30 was a large clean-up release. It dropped Node 14, 16, 19 and 21, upgraded jest-environment-jsdom from jsdom 21 to 26, and replaced module resolution with unrs-resolver, which the team says is more standards-compliant and faster. It also removed every alias matcher, so toBeCalled becomes toHaveBeenCalled and toThrowError becomes toThrow; the Jest team points to an eslint-plugin-jest autofixer for the rename.

The release post reports one large TypeScript application going from about 1,350 seconds and 7.8 GB peak memory to about 850 seconds and 1.8 GB. ESM support improved, with import.meta and native .mts and .cts handling, but the documentation still labels ESM support experimental and requires the --experimental-vm-modules flag. In ESM, module mocks use jest.unstable_mockModule followed by a dynamic await import(), because static imports are evaluated before jest.mock hoisting can apply.

Vintage silver stopwatch with a white dial and chain against a black background
Measure your own suite: speed differences depend heavily on transforms, isolation and environment.

The same test in both runners

Here is a small module that converts a price using an exchange-rate service, and a test that mocks the service. In Vitest:

TypeScript
// src/price.test.ts (Vitest)
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { priceInEur } from './price.js';
import { getRate } from './rates.js';

vi.mock('./rates.js', () => ({ getRate: vi.fn() }));

describe('priceInEur', () => {
  beforeEach(() => {
    vi.mocked(getRate).mockResolvedValue(0.9);
  });

  it('converts and rounds to cents', async () => {
    await expect(priceInEur(10.99)).resolves.toBe(9.89);
    expect(getRate).toHaveBeenCalledWith('USD', 'EUR');
  });
});

The Jest version for a CommonJS or Babel-transformed project differs only in where the API comes from:

TypeScript
// src/price.test.ts (Jest, globals enabled by default)
import { priceInEur } from './price';
import { getRate } from './rates';

jest.mock('./rates', () => ({ getRate: jest.fn() }));

describe('priceInEur', () => {
  beforeEach(() => {
    jest.mocked(getRate).mockResolvedValue(0.9);
  });

  it('converts and rounds to cents', async () => {
    await expect(priceInEur(10.99)).resolves.toBe(9.89);
    expect(getRate).toHaveBeenCalledWith('USD', 'EUR');
  });
});

Most suites migrate mechanically. The differences that cause real failures are in mocking semantics, covered next.

Mocking differences that break migrations

The Vitest migration guide lists these behaviour differences from Jest:

  • Factory return values. In Jest, whatever a jest.mock factory returns becomes the default export. In Vitest the factory must return an object with each export named, so () => 'hello' becomes () => ({ default: 'hello' }).
  • __mocks__ folders are not applied automatically; Vitest uses them only when you call vi.mock(). Call it in a setup file to apply a mock everywhere.
  • jest.requireActual becomes await vi.importActual(), which is asynchronous.
  • mockReset differs. Jest replaces the implementation with one that returns undefined; Vitest restores the original implementation passed to vi.fn(impl).
  • Legacy fake timers and done callbacks are not supported. Return a promise or use async instead.
  • Hooks run as a stack by default in Vitest, and a beforeEach may return a teardown function. Set sequence.hooks: 'list' to match Jest's order.
  • Types such as jest.Mock become import type { Mock } from 'vitest'.
TypeScript
// The mockReset difference, verified in both runners
const add = vi.fn((a: number, b: number) => a + b);
add.mockReturnValue(0);
add(1, 2);      // 0
add.mockReset();
add(1, 2);      // Vitest: 3 (original implementation)
                // Jest:   undefined
Scientist in a laboratory using a pipette to transfer a sample into a flask
Mocks control what goes into the code under test, and the two runners handle them differently.

Which one should you choose?

Choose Vitest when

  • The project is ESM-first, or already uses Vite for a front end.
  • You want TypeScript tests without configuring a separate transformer.
  • You want component tests in a real browser rather than jsdom.
  • You are starting a NestJS 12 project as ESM: the NestJS v12 migration guide says generated ESM projects use Vitest, while CommonJS projects keep Jest.

Stay on Jest when

  • You have a large CommonJS suite that is stable and fast enough.
  • You depend on automatic __mocks__, jest.requireActual patterns or Jest-specific plugins and custom environments.
  • Your runtime is older than Node.js 22.12, which Vitest 5 does not support.

Speed is not a reliable tiebreaker in the abstract. Both projects publish impressive numbers for particular suites; what matters is how your tests use transforms, isolation and DOM environments. Run a representative slice of your suite in both before deciding.

A migration checklist from Jest to Vitest

  1. Install vitest and add a vitest.config.ts, or a test block in your Vite config. Set environment: 'jsdom' or 'happy-dom' for DOM tests.
  2. Decide on globals. Either set globals: true and add vitest/globals to your TypeScript types, or import from vitest in each file. Note that Testing Library's automatic cleanup relies on globals.
  3. Replace jest. with vi. and fix mock factories that return a bare default export.
  4. Replace jest.requireActual with await vi.importActual, and done callbacks with promises.
  5. Search for mockReset and check whether tests relied on it returning undefined.
  6. Move __mocks__ usage into explicit vi.mock() calls or a setup file.
  7. Run the suite, then run it again with --repeats on the tests that fail intermittently.

Gotchas

  • Missing await on resolves and rejects. In Vitest 5 these now fail, which is correct but can surface dozens of latent bugs at once.
  • Relying on accumulated mock calls. With clearMocks on by default, a test that asserted call counts across tests will start failing.
  • Jest and ESM-only dependencies. The NestJS v12 guide warns that Jest can load its ESM-only packages only on Node.js 24.9 or later; the same pressure applies to other ESM-only libraries.
  • Type checking. Vitest and babel-jest strip types without checking them, so a test run is not a type check. Keep tsc --noEmit in CI.

What to do next

If you are on Jest and happy, upgrade to the latest 30.x, run the alias autofixer, and revisit the question when ESM-only dependencies start to hurt. If you are starting fresh or already fighting --experimental-vm-modules, pilot Vitest 5 on one package, work through the checklist above, and migrate the rest once the pilot's CI times and flake rate look right.

Frequently asked questions

  • Is Vitest compatible with Jest?

    The APIs are deliberately similar: describe, it, expect and most matchers work the same way, and jest.fn maps to vi.fn. Behaviour differs in places, notably mock factories, mockReset, __mocks__ loading, hooks order and done callbacks.

  • Does Jest 30 support ES modules?

    Jest 30 improved ESM support, but its documentation still describes it as experimental. You must run Node with --experimental-vm-modules and use jest.unstable_mockModule with dynamic imports for module mocks.

  • What Node.js version does Vitest 5 need?

    Vitest 5 requires Node.js 22.12.0 or later and Vite 6.4.0 or later.

  • Which test runner does NestJS 12 use?

    According to the NestJS v11 to v12 migration guide, newly generated ESM projects use Vitest and CommonJS projects use Jest; nest upgrade bumps Jest to v30.

  • Why did my Vitest tests start failing after upgrading to 5.0?

    The most common causes are the new clearMocks: true default, unawaited resolves or rejects assertions that now fail, and test name filters affected by the new ' > ' separator.

  • Is Vitest faster than Jest?

    Often, especially for TypeScript and ESM projects, but it depends on your suite. Benchmark a representative set of your own tests in both runners before migrating for speed alone.

Advertise on CloudSoftZone

Your next customer could start right here.

Introduce your tool, product or business to readers exploring technology.

Advertise with us

Explore placements and request a private quote

Sources

  1. Announcing Vitest 5.0 — Vitest, 2026-09-03
  2. Migration Guide — Vitest
  3. Migrating from Jest — Vitest
  4. Jest 30: Faster, Leaner, Better — Jest, 2025-06-04
  5. From v29 to v30 — Jest
  6. ECMAScript Modules — Jest
  7. Migration guide (v11 to v12) — NestJS

Comments

No comments yet. Be the first to join the conversation.

Leave a comment

Every comment is read by a moderator before it appears. Your email address is never published; it is used only to contact you about this comment.

Never published. Used only to contact you about this comment.

Loading the security check…

Comments are moderated before publication.