The Consolidation of JavaScript Testing: How Vitest Won
TL;DR
Vitest won new project adoption. Jest wins by legacy volume. In 2026, ~70% of new JavaScript projects choosing a test runner pick Vitest. Jest still has ~18M weekly downloads vs Vitest's ~8M, but the gap is closing fast. The story: Jest was built for CommonJS + Babel in 2014. Vitest was built for ESM + Vite in 2021. When the ecosystem moved to ESM, Vite, and TypeScript-first development, Vitest was already there. Jest scrambled to add ESM support, but Vitest's native integration was simply better.
Key Takeaways
- Vitest: ~8M weekly downloads — growing ~150% YoY; dominant for new projects
- Jest: ~18M weekly downloads — still dominant by volume; legacy codebases
- Vitest new project share — ~70% of new projects choosing a test runner in 2026
- Key differentiator — Vitest shares your Vite config; Jest requires separate Babel/transform config
- API compatibility — Vitest's API is Jest-compatible; migrations take 1-2 hours
Why Vitest Won
1. Native ESM and TypeScript
// Jest with TypeScript — requires transform setup
// jest.config.js
module.exports = {
preset: 'ts-jest',
// OR:
transform: {
'^.+\\.tsx?$': ['babel-jest', { presets: ['@babel/preset-typescript'] }],
},
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1', // Path aliases need manual config
},
};
// package.json deps: jest, @types/jest, ts-jest or babel-jest, @babel/preset-typescript
// Total: 5+ packages for TypeScript support
// Vitest with TypeScript — just works
// vitest.config.ts (or vite.config.ts)
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
// That's it. TypeScript works natively.
},
});
// package.json deps: vitest
// Total: 1 package
2. Shared Vite Config (Zero Duplication)
// Vitest reuses your Vite config — no duplication
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import tsconfigPaths from 'vite-tsconfig-paths';
export default defineConfig({
plugins: [react(), tsconfigPaths()],
resolve: {
alias: { '@': '/src' },
},
// test section only for Vitest — everything else is shared
test: {
environment: 'happy-dom',
globals: true,
},
});
// Jest equivalent requires duplicating ALL of this:
// - Transform config (must match Vite's plugin list)
// - Path aliases (moduleNameMapper)
// - Mock handling (moduleFileExtensions)
// Every Vite plugin needs a Jest equivalent
3. Speed
# Cold start comparison (medium project, 200 test files)
Jest (ts-jest): ~6,200ms
Jest (babel-jest): ~4,800ms
Vitest: ~520ms (10x faster cold start)
# Watch mode (single file change):
Jest: ~800ms (re-processes affected files)
Vitest: ~80ms (Vite's incremental HMR approach)
# The developer loop impact:
# Save → test feedback in 80ms (Vitest) vs 800ms (Jest)
# That's the difference between "instant feedback" and "wait to see result"
4. In-Source Testing
// Vitest's unique feature: tests alongside source code
// src/lib/health-score.ts
export function calculateHealthScore(pkg: Package): number {
// ... implementation
}
// In-source tests (only run during `vitest`, not in production build)
if (import.meta.vitest) {
const { it, expect } = import.meta.vitest;
it('returns 100 for perfect package', () => {
expect(calculateHealthScore({ weeksSinceLastRelease: 1 })).toBe(100);
});
}
The Migration Path
From Jest to Vitest (1-2 hours)
# Step 1: Install Vitest
npm uninstall jest ts-jest babel-jest @types/jest
npm install -D vitest @vitest/coverage-v8 happy-dom
# Step 2: Create vitest.config.ts (or update vite.config.ts)
# test: { globals: true, environment: 'happy-dom' }
# Step 3: Update package.json scripts
# "test": "vitest" (was: "jest")
# "test:coverage": "vitest run --coverage"
# Step 4: Run vitest — 90% of Jest tests work unchanged
npx vitest run
# Step 5: Fix differences (usually minimal)
// Common differences to fix:
// 1. jest.mock → vi.mock
// Before: jest.mock('./api');
// After: vi.mock('./api');
// 2. jest.fn() → vi.fn()
// Before: const mock = jest.fn();
// After: const mock = vi.fn();
// 3. jest.useFakeTimers → vi.useFakeTimers
// API is identical, just rename jest → vi
// 4. Import vi (if not using globals)
import { vi, describe, it, expect } from 'vitest';
// 5. jest.spyOn → vi.spyOn (identical API)
const spy = vi.spyOn(console, 'log');
Download Trend Analysis
Weekly downloads (actual data):
2022 Q1:
Jest: 15M
Vitest: 300K (launched Feb 2021)
Ratio: 50:1
2023 Q1:
Jest: 17M
Vitest: 2M
Ratio: 8.5:1
2024 Q1:
Jest: 18M
Vitest: 5M
Ratio: 3.6:1
2026 Q1:
Jest: 18M (plateaued)
Vitest: 8M (still growing)
Ratio: 2.25:1
Projection: Vitest surpasses Jest downloads by 2027-2028
What's Still Better in Jest
Vitest didn't win everything:
// Jest advantages in 2026:
// 1. Mature ecosystem — more third-party matchers
import { toHaveNoViolations } from 'jest-axe'; // Accessibility testing
import { toMatchInlineSnapshot } from 'jest-snapshot'; // Better snapshots
// Vitest supports most, but jest-specific matchers need @testing-library/jest-dom
// 2. Snapshot testing — Jest's .toMatchSnapshot() is the gold standard
// Vitest supports it but the UX is slightly different for inline snapshots
// 3. React Native testing — Jest is still the standard
// @testing-library/react-native, jest-react-native — all Jest-first
// Vitest for React Native: possible but less tooling
// 4. Some CI environments — Jest's error output is more familiar to teams
// Minor, but real: teams may need to update CI scripts
The E2E Side: Playwright Won Faster
While Vitest vs Jest played out slowly, the E2E battle was decisive:
E2E testing download trends (2026):
Playwright: ~5M weekly downloads (+300% since 2022)
Cypress: ~5M weekly downloads (flat since 2023)
New project selection:
Playwright: ~65% of new projects choosing E2E testing
Cypress: ~30%
WebdriverIO: ~5%
Playwright's key advantages:
✅ Cross-browser (Chromium, Firefox, WebKit)
✅ Multi-tab, multi-window, mobile viewport
✅ Auto-waiting (no flaky timeouts)
✅ Built-in codegen (record tests by clicking)
✅ Trace viewer (visual replay of failures)
✅ Free (Cypress component testing is paywalled)
Testing Stack Recommendations for 2026
| Project Type | Unit Tests | Component Tests | E2E |
|---|---|---|---|
| New React/Vite | Vitest | Vitest + Testing Library | Playwright |
| New Next.js | Vitest | Vitest + Testing Library | Playwright |
| Existing Jest codebase | Migrate to Vitest OR stay Jest | Same | Migrate to Playwright |
| React Native | Jest | Jest + Testing Library | Detox |
| Node.js API | Vitest | N/A | Playwright or Supertest |
| Library | Vitest | Vitest | N/A (consumers test) |
Compare testing library package health on PkgPulse.
See the live comparison
View vitest vs. jest on PkgPulse →