CLI
vitest-native ships a small CLI for the moments where a config file and docs page aren't enough: starting out, checking an environment, and migrating from Jest.
npx vitest-native init # write a ready-to-run vitest config
npx vitest-native doctor # diagnose peers, engine, presets, RNTL/Node
npx vitest-native migrate # analyze your jest config → migration report
npx vitest-native compare # run the suite under Jest and Vitest, list the differencesAll commands accept --root <dir> (default: the current directory). A flag the command does not take is refused with the valid ones listed, so a typo such as --wirte stops the run instead of quietly doing something else.
init
Writes vitest.config.mts (or .mjs in JS-only projects) with the zero-config plugin. Refuses to overwrite an existing config unless --force, which replaces the config file Vitest loads, under its own name (a CommonJS vitest.config.cjs or .cts is refused instead). Vitest reads a vite.config.* only when there is no vitest.config.*, and does not merge the two, so when a vite.config.* exists, init says that the new file takes over from it and shows how to extend it with mergeConfig.
npx vitest-native init # plain config
npx vitest-native init --jest-compat # + jest-compat layer, for migrated suitesThe --jest-compat variant writes the same pieces the Jest migration guide documents: jestMockTransform() beside reactNative(), globals: true, the @jest/globals alias, and the jest-compat setup file. It leaves the engine on its default (auto), where the guide's example names engine: 'native'.
doctor
Read-only environment diagnosis:
- Node version against the supported range (22.22.3+ on Node 22, 24.12+ on Node 24, or 26+).
- Required peers (vitest, vite, react) against the supported ranges, including the per-major Vite security floors.
- Which engine
autoresolves to for this project, and why. - Every auto-detected preset (installed package → preset), and installed packages that run their own test mode under Vitest instead (react-native-mmkv 3+).
- Expo presence, and how Expo modules run under the engine it resolves to.
- Babel plugins from your Babel config that a vitest-native run does not apply (warnings only; a macro plugin is reported as required). To read them, doctor loads
babel.config.js/.babelrc.jsthe way Babel would (withNODE_ENV=test); ESM and TypeScript configs are only scanned as text. - Whether a vitest config exists and uses vitest-native.
Exits non-zero when it finds a blocking problem, so it can gate CI setup jobs.
migrate
Reads your Jest configuration (package.json#jest, or the first jest.config.{js,ts,mjs,mts,cjs,cts,json} in Jest's own order) and reports, key by key:
- Mapped automatically — keys the suggested config absorbs (
setupFilesAfterEnv→setupFiles, path aliases →resolve.alias,transformIgnorePatternsallowlists →reactNative({ transform: [...] })only for packages that ship JSX or Flow in a.jsfile and are not already compiled by the engine,testTimeout,clearMocks, …). The test set is Jest's effective one:testMatchfrom your config, else from the preset (read from the installed preset), else Jest's defaults, narrowed bytestPathIgnorePatterns,modulePathIgnorePatternsandmoduleFileExtensions. Flags thetestscript passes to Jest (--testTimeout,--maxWorkers,--runInBand,--config) are applied the way Jest applies them, over the config. - Babel — plugins from your Babel config that babel-jest applied and vitest-native does not, sorted into required, mapped, not needed and unknown (see Babel plugins).
- Covered by presets — delete — manual
__mocks__/files and setup lines that the auto-detected presets replace. - Needs your attention — regex module mappers, fake-timer config, unknown keys: things a human should decide.
- Dropped — Jest keys with no vitest-native equivalent needed.
It ends with a complete suggested config. Dry-run by default; --write saves it, and refuses to overwrite an existing config unless --force (which replaces it as init --force does). It never edits test files — top-level jest.mock calls are handled at runtime by jestMockTransform().
TypeScript configs load through Node's own type stripping. Jest loads them through ts-node instead, so a config that relies on it (an extensionless relative import, an enum, a path alias) can't be loaded, and neither can a config that uses top-level await or exports a function. The report then names the file and Node's reason, instead of guessing.
compare
Runs your project's own Jest, then its own Vitest, and lists every test that passes under Jest and not under Vitest, grouped by its first error. Use it while both runners are in place; see running Jest and Vitest side by side.
npx vitest-native compare
npx vitest-native compare --json compare.json # a report to attach to an issueNeither run writes snapshots, and compare fails if a run changes a file in the project. With no Vitest config yet, it uses the config migrate would write, without saving it. It exits 0 when every test that passes under Jest also passes under Vitest and vitest run exits 0, and 1 when not. It exits 2 when a runner could not run, when the project changed, or when no test passed under Jest, and 130 when interrupted.
| Flag | Does |
|---|---|
--jest-args "<args>", --vitest-args "<args>" | Pass extra arguments to one runner. Only filters, timeouts and switches that cannot write files are accepted. |
--vitest-config <file> | Use this Vitest config. |
--jest-results <file> | Read a Jest --json report, such as one from CI, instead of running Jest. |
--jest-root <dir> | The directory that report was made in. Found from the report when omitted. |
--json <file> | Write a report of test names, results and error messages, with stack traces, code frames and absolute paths removed. Read it before you attach it to an issue: a message still contains whatever a test put in it. |
--top <n> | How many error groups and files to list (default 10). |
--timeout <seconds> | The time limit for each runner (default 1800). |
--show-output | Stream both runners' output. |
--strict | Confirm a parametrised test whose title Vitest printed as %p only by its title, never by its row position. |
--hot | Compare the suite per file with hotRuntime: 'auto' instead of Jest with Vitest. See below. |
--runs <n> | With --hot: runs per mode (default 3; the first warms caches and is not timed). |
--seed <n> | With --hot: the seed for the shuffled hot run (random by default, always printed). |
--no-shuffle | With --hot: skip the shuffled hot run. |
compare --hot
npx vitest-native compare --hotRuns your Vitest config per file and with hotRuntime: 'auto', then compares every test's result. Jest is not run. The runs are, in order:
- per file three times;
- hot three times;
- one hot run with files shuffled (
--seed, printed); - one hot run at
--maxWorkers=2, where more files share a worker.
Tests are matched by file, full name and occurrence. A failure compares by its kind (an assertion error, a timeout, an error code), not its message. Also compared: errors outside tests, and snapshots written or left obsolete.
- No difference: it prints
No difference in N per-file and M hot runsand exits 0. Then the timing: each mode's median wall time and range over its timed runs. The first run of each mode only warms caches, and the shuffled and--maxWorkers=2runs are checks; the listing marks all of these as not timed. It says hot is faster or slower only from at least two timed runs per mode whose ranges don't overlap. With fewer runs, or overlapping ranges, it makes no speed claim. When hot is faster, you can addhotRuntime: 'auto'toreactNative({ … }). - Differences: it lists each test that differs, or that varies between hot runs, and exits 1. A test that already varies between per-file runs is reported separately, and hot may only produce results the per-file runs also produced.
- No verdict (exit 2): a hot run did not actually run hot, because
'auto'declined and printedhot runtime off: …; or a run failed to start or timed out; or the runs changed project files.
Two runs cannot rule out a rare flake: raise --runs for more confidence. --json records every run's results, the versions and the worker counts.