Overview
Use Vitest for new projects. It shares the Vite config, natively supports ESM and TypeScript without transforms, and runs tests in parallel with HMR-speed feedback. Use Jest when the project predates Vite, runs on CommonJS, or depends on Jest-specific plugins such as jest-circus, jest-environment-jsdom with legacy setup, or community plugins that lack Vitest ports. See vite-vs-webpack for the upstream bundler decision.
When Vitest wins
Vitest is the right test runner for projects that already use Vite or are being built from scratch.
- Zero config with Vite: Vitest reads
vite.config.tsdirectly. Aliases, module resolution, and plugins defined there apply to tests without duplication. - Native ESM: imports and dynamic
import()work without Babel transforms or Jest’s experimental--experimental-vm-modulesflag. - TypeScript out of the box: no
ts-jest, nobabel-jest. Vitest strips types natively using esbuild. - Speed: Vitest runs in Vite’s worker pool. A cold run on a 500-test suite is typically 2 to 4 times faster than Jest on the same hardware, and re-runs with
--watchare near-instant. - Compatible API:
describe,it,expect,vi.mock,vi.spyOnmap directly to Jest equivalents. Migration of test bodies is mostly a find-and-replace ofjest.*tovi.*. - UI mode:
vitest --uiopens a browser panel with test results, coverage, and logs. Useful when debugging flaky tests.
When Jest wins
Jest remains the correct choice in three situations.
- Legacy CommonJS codebase not using Vite: Jest’s transform pipeline handles CommonJS naturally. Migrating to Vitest also means migrating the module system, which is a separate risk.
- Snapshot testing with custom serializers: Jest’s snapshot ecosystem and
jest-snapshottooling are mature. Vitest snapshots work but some third-party serializers have no Vitest port. - Specific plugin dependencies:
jest-axefor accessibility assertions,jest-fetch-mock, or enterprise CI plugins that hook into Jest’s reporter API. Check Vitest compatibility before committing. - React Native: Jest is the standard test runner for React Native. Vitest does not support React Native’s Metro bundler environment.
Trade-offs at a glance
| Dimension | Vitest | Jest |
|---|---|---|
| Config | Shares vite.config.ts | Separate jest.config.* |
| ESM support | Native | Experimental flag or Babel transform |
| TypeScript | Native (esbuild strip) | ts-jest or babel-jest |
| Cold run speed | Fast (esbuild, parallel workers) | Slower on large suites |
| Watch mode | HMR-speed re-runs | File-watch re-runs; slower |
| Plugin ecosystem | Growing; most common cases covered | Mature; deep third-party support |
| React Native | Not supported | Standard |
| API compatibility | Jest-compatible; vi.* not jest.* | Canonical |
| Coverage | Built-in (v8 or istanbul) | --coverage via istanbul |
| Community size | Growing rapidly | Large; established |
| Snapshot testing | Supported; fewer serializer options | Mature ecosystem |
Migration cost
Jest to Vitest migration is typically low-effort for Vite projects.
- Replace
jestwithvitestinpackage.json. Updatejest.config.*to atestblock invite.config.ts. - Global
jest.*calls becomevi.*:jest.mocktovi.mock,jest.spyOntovi.spyOn,jest.fntovi.fn. A codemod or find-and-replace handles 90 percent of cases. @testing-librarypackages work unchanged.jsdomis still the default environment for browser-like tests.- Watch for
jest.useFakeTimerssemantics; Vitest’s fake timer API is mostly compatible but has minor differences aroundsetSystemTime. - Effort: one to three engineer-days for a 200-test suite with no exotic plugins.
Recommendation
- New Vite project: Vitest. No exceptions for greenfield work.
- New project without Vite: Vitest still works but requires configuring the environment separately. Evaluate whether adopting Vite as well is worth it; often it is.
- Existing Jest project on CommonJS: stay on Jest. Migrate when the project migrates to ESM or Vite.
- React Native: Jest.
- Mixed monorepo: run Vitest in the web packages and Jest in the React Native package. They coexist.