Vitest
A Vite-native test runner with a Jest-compatible API — faster startup, native ESM and TypeScript, and the cases where it is the better default.
2 min read · updated 19 September 2026
Vitest runs tests through Vite's transform pipeline. If a project already builds with Vite, its test runner now shares that configuration — the same aliases, the same plugins, the same TypeScript and JSX handling, with no second toolchain to keep in sync.
That is the whole pitch, and for a Vite project it is a strong one.
#What it looks like
Deliberately familiar:
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { applyDiscount } from './pricing';
describe('applyDiscount', () => {
it.each([
[12_000, 2_400],
[10_000, 2_000],
[9_999, 0]
])('discounts %i by %i', (subtotal, expected) => {
expect(applyDiscount({ subtotalCents: subtotal }, policy).discountCents).toBe(expected);
});
});vi replaces jest as the mocking namespace and the methods line up:
vi.fn, vi.spyOn, vi.mock, vi.useFakeTimers. Most
Jest test bodies port by changing an import.
#Configuration
// vite.config.ts — one config for build and test
import { defineConfig } from 'vitest/config';
import react from '@vitejs/plugin-react';
import { resolve } from 'node:path';
export default defineConfig({
plugins: [react()],
resolve: { alias: { '@': resolve(__dirname, './src') } }, // shared with the build
test: {
globals: true, // describe/it/expect without importing
environment: 'node',
setupFiles: ['./vitest.setup.ts'],
coverage: { provider: 'v8', reporter: ['text-summary', 'lcov'] },
// The equivalent of Jest projects: DOM only where it is needed.
workspace: [
{ test: { name: 'unit', environment: 'node', include: ['src/lib/**/*.test.ts'] } },
{ test: { name: 'dom', environment: 'jsdom', include: ['src/components/**/*.test.tsx'] } }
]
}
});The alias is defined once and used by both the application build and the
tests. In a Jest setup the same information lives in moduleNameMapper and
drifts.
#Where it is genuinely better
Startup. No separate transform step, and modules are transformed on demand. On a large suite this is the difference between a two-second and a twelve-second cold start, which matters for watch-mode discipline.
Native ESM. Jest's ESM support has improved and is still the most common source of configuration pain in a modern JavaScript project. Vitest was built for it.
Watch mode. Vite's module graph means only genuinely affected tests rerun. It is noticeably sharper than Jest's heuristic.
In-source testing. Tests can live in the module they test, stripped from
the production build by define:
export function slugify(text: string) {
return text.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
}
// Removed from the production bundle by `define: { 'import.meta.vitest': 'undefined' }`
if (import.meta.vitest) {
const { it, expect } = import.meta.vitest;
it('slugifies', () => expect(slugify('Hello, World!')).toBe('hello-world'));
}Charming for small pure utilities; do not build a whole suite this way.
Browser mode. Runs the same tests in a real browser through Playwright rather than jsdom, which removes an entire class of "works in jsdom, breaks in Chrome" surprises for component tests.
#Where Jest still wins
- Ecosystem. More matchers, more reporters, more integrations, and vastly more written about it when something goes wrong at 5pm.
- Non-Vite projects. Adding Vite purely to get Vitest is a real cost.
- Node-heavy backends. Jest's Node support is more battle-tested for server code that never touches a bundler.
- React Native and some monorepo tooling still assume Jest.
#Migrating
npm i -D vitest @vitest/coverage-v8- import { jest } from '@jest/globals';
+ import { vi } from 'vitest';
- jest.mock('./rates');
+ vi.mock('./rates');
- jest.useFakeTimers();
+ vi.useFakeTimers();Then move moduleNameMapper to resolve.alias, testEnvironment to
test.environment, and projects to test.workspace. For a suite of a few
hundred files this is usually an afternoon. The parts that resist are
anything depending on Jest internals, custom transforms, or timer semantics
at the edges.
The honest summary: if you are on Vite, use Vitest. If you are not, the
migration is real work for a speed gain you may be able to get more cheaply
by replacing ts-jest with @swc/jest.
Common questions
- Is Vitest a drop-in replacement for Jest?
- Close, not exact. The assertion and mocking APIs are deliberately compatible, so most test bodies port unchanged. What differs is configuration, some timer behaviour, and anything relying on Jest internals or Babel transforms.
- When should I choose Vitest over Jest?
- When the project already uses Vite, when it is ESM-only, or when Jest's transform cost is dominating your suite time. Outside those cases Jest's larger ecosystem is worth more than the startup difference.
- Does Vitest do browser testing?
- It has a browser mode that runs tests in a real browser via Playwright or WebDriver, which is a genuine alternative to jsdom for component tests. It is not a replacement for an end-to-end tool.
Runnable samples for this page
last test results ↗- TypeScript (Vitest)
vitest/src/tools/vitest
Working tests, not fragments — they run in CI on every push to 8exgh/endtoendtester-samples.
Was this page useful?
Related topics
- JestThe default JavaScript test runner — configuration, projects, snapshot testing, fake timers, and the choices that keep a large Jest suite fast.
- Writing Tests in TypeScriptThe TypeScript testing toolchain — Jest or Vitest, typed doubles, async idioms, and the type-level tricks that make tests both safer and more readable.
- Testing ReactComponents, hooks, context, server components and async state — what to test in a React application, what to leave alone, and how to avoid act warnings.
- Testing Vue.jsVue Test Utils and Testing Library, nextTick and reactivity timing, Pinia stores, composables and the mount-versus-shallowMount decision.
- Code CoverageWhat the percentage measures, why it is a finding tool rather than a target, how to collect it in each ecosystem, and how to gate on it without causing harm.
- PlaywrightThe default browser automation tool in 2026 — auto-waiting locators, tracing, sharding and fixtures — with the configuration that matters and the mistakes that still cause flakiness.
- Snapshot TestingRecording output and comparing it on every run — where snapshots earn their place, the approval reflex that destroys their value, and better alternatives.