For a new Next.js 16 app in late 2026, start with Vitest. For an existing app with hundreds of Jest tests that pass and a CI job nobody complains about, keep Jest 30 and spend the migration week on something users will notice. That is the short answer. The longer one depends on three questions: how your code ships as modules, how much mocking you do, and whether you want component tests in a real browser.
Both runners changed a lot in the last eighteen months, so most comparisons you will find are out of date. Here is where each one stands now and how I would set them up on an App Router project.
Where each runner stands in November 2026
Vitest shipped 4.0 on October 22, 2025, which took Browser Mode out of experimental. Vitest 5.0 followed on September 3, 2026 with a release focused on speed: a persistent file system module cache, shared Vite servers across inline projects, fewer round trips to workers, and faster coverage. It also turned clearMocks on by default and added vi.when for per-argument mock behavior. Vitest 5.1 landed on November 2, 2026 with Trace View on by default in a failure-only mode, plus --last-failed. Vitest 5 needs Node.js 22.12 or newer and Vite 6.4 or newer.
Jest shipped 30.0 in June 2025 with a new resolver, lower memory use, and native support for .mts and .cts files. The 30.x line kept moving through 2026: 30.4 (May) rewrote the module runtime and added require(esm) on Node 24.9+, and 30.5 (August) cut warm module resolution to about a third of its old cost and made automocks and __mocks__ folders work for ESM. Native ESM support is still labeled experimental, still needs --experimental-vm-modules, and ESM mocking still goes through jest.unstable_mockModule.
Next.js documents both. The official guides cover next/jest and a Vitest setup with @vitejs/plugin-react, and both pages carry the same warning: neither runner supports async Server Components. Treat that as a constraint on the framework, not on the runner.
The real difference: how your code becomes modules
Most of the friction people report with Jest on a modern Next.js app comes from modules. Your app code is ESM. Many packages you depend on now ship ESM only. Jest still runs on its own module system, so it either transforms everything to CommonJS through next/jest (which uses the Next.js SWC compiler) or runs native ESM in a mode that its own docs call experimental. When a dependency ships untranspiled ESM, you end up adding it to transformIgnorePatterns and hoping the next upgrade does not break it.
Vitest runs your tests through Vite's module runner. ESM is the default, TypeScript and JSX work without a separate transformer, and path aliases come from the same vite-tsconfig-paths plugin you would use elsewhere. That does not mean zero setup, since you still need the React plugin and a DOM environment, but the module story matches what your bundler does.
Jest 30.5 narrowed this gap more than most people realize. If you are on Node 24.9 or newer and already on Jest 30, try upgrading to the latest 30.x before you decide Jest cannot handle your dependencies.
Speed: measure your own suite
Both projects publish numbers, and both numbers are honest about their own setup. The Jest 30 announcement cited one large TypeScript app whose server tests went from about 1,350 seconds to 850 seconds and from 7.8GB to 1.8GB of peak memory. Vitest 5 built a public benchmark repo with generated apps from 5 files to 1,280 modules, across every pool and DOM environment.
Neither number tells you what will happen to your suite. In practice the biggest wins come from the same places in both runners:
- Barrel files. A test that imports one helper from an
index.tsthat re-exports 200 modules loads all 200. Both projects call this out. - Open handles. Timers and database connections that never close slow Jest down and can make either runner hang.
- Isolation settings. Turning off per-file isolation is fast and risky. Only do it when your tests do not share global state.
If you want a real comparison, port 20 representative test files, run both runners three times on the same CI machine, and compare wall time and peak memory. That takes an afternoon and beats any blog chart, including mine.
Mocking: similar API, different rules
Vitest copied most of the Jest API on purpose, so describe, it, expect, and snapshot tests read the same. The differences that bite during a migration are small and specific:
- Globals are off by default in Vitest. Import
testandexpectfromvitest, or setglobals: trueand add the types. jest.fn()becomesvi.fn(), andjest.mock()becomesvi.mock(). Both get hoisted, but in Vitest a mock factory cannot use variables from the outer scope unless you wrap them invi.hoisted.vi.importActualis async, so partial mocks need anawait.- With Vitest 5,
clearMocksis on by default. Tests that relied on call counts leaking between tests will fail, and that is usually a real bug you found for free.
For next/navigation, next/headers, and next/font, next/jest gives you some mocks for free, such as styles, images, and fonts. In Vitest you write those mocks yourself in a setup file. It takes about ten lines, but it is ten lines you have to own.
Browser Mode is the reason I lean toward Vitest
JSDOM is a simulation. It has no layout, no real focus handling, and no real pointer events. For a component that opens a popover, traps focus, or measures its own size, a JSDOM test can pass while the real browser fails.
Vitest Browser Mode runs the same component test in Chromium, Firefox, or WebKit through Playwright or WebdriverIO. Since 4.0 it is stable, and Vitest 5 added Trace View, so a failed step shows you the page as it was at that moment. Jest has no equivalent; you would reach for Playwright component testing or a full E2E run instead.
This is also how I would split tests in a Next.js project. Pure functions, hooks, Server Actions with their data layer mocked, and synchronous components go in unit tests. Interactive client components go in Browser Mode. Async Server Components and anything that depends on routing, caching, or cookies go in Playwright E2E against a running next start.
How I would set up each one
For a new Next.js 16 app with Vitest:
- Install
vitest,@vitejs/plugin-react,vite-tsconfig-paths,jsdom, and Testing Library. - Create
vitest.config.mtswith the React and tsconfig paths plugins andenvironment: 'jsdom'. - Add a setup file that mocks
next/navigationand imports@testing-library/jest-dom/vitest. - Add a second project for Browser Mode with
@vitest/browser-playwrightand keep interactive component tests there.
1import { defineConfig } from 'vitest/config'2import react from '@vitejs/plugin-react'3import tsconfigPaths from 'vite-tsconfig-paths'45export default defineConfig({6 plugins: [tsconfigPaths(), react()],7 test: {8 environment: 'jsdom',9 setupFiles: ['./vitest.setup.ts'],10 },11})
For an existing Jest suite I would keep:
- Upgrade to the latest Jest 30.x and Node 24 LTS.
- Keep
next/jestso the SWC transform and asset mocks stay in sync with Next.js. - Fix barrel imports and open handles before you blame the runner.
- Move only the tests that need a real browser to Playwright.
My pick
Pick Vitest if the project is new, ships ESM dependencies, already uses Vite somewhere, or needs component tests in a real browser. Pick Jest if the suite is large and stable, the team knows its mocking rules well, or you depend on Jest-specific tools such as custom environments or reporters that have no Vitest port yet.
Whichever you pick, do not ask a unit test runner to cover async Server Components. Put those behind a small Playwright suite and keep the unit layer fast.
Sources
- Vitest 4.0 is out, Vitest 5.0 is out, and Vitest 5.1 is out on the Vitest blog.
- Jest 30: Faster, Leaner, Better, plus the v30.4.0 and v30.5.0 release notes.
- Jest docs on ECMAScript Modules.
- Next.js testing guides for Vitest and Jest.