Migrating from vitest-react-native
If you used vitest-community/vitest-react-native and it stopped working on a newer Vitest, this is the continuation you're looking for.
The short version: it's the same idea, maintained. The original plugin and vitest-native's default engine share the same architecture — externalize React Native to Node, strip its Flow types through a require hook, and mock only the thin native boundary (View / Text / UIManager / NativeModules / …). The original simply went unmaintained through Vitest's module-runner rewrites (v2 → v3 → v4) and bit-rotted. vitest-native tracks current Vitest and React Native, and a CI matrix gates it across both so it stays alive.
For the common case — render real RN with RNTL, mock your own modules with vi.mock — migration is a config swap.
1. Swap the package
# remove the old plugin
npm rm vitest-react-native @vitejs/plugin-react
# add this one (+ the babel preset it uses to strip RN's Flow types)
npm i -D vitest-native @react-native/babel-preset @babel/coreKeep your existing vitest, vite, react, react-native, and @testing-library/react-native. Requirements: Vitest 4.x or 5.x, Vite ^6.4.2, ^7.3.2, or ^8.0.5, the React version required by your RN release, React Native 0.81–0.88 (validated in CI), and RNTL 12–14. RNTL 14 uses async rendering APIs. vitest-native supports Node 22.13+ on Node 22, or 24.3+.
2. Update vitest.config
Before (vitest-react-native):
import reactNative from 'vitest-react-native'
import react from '@vitejs/plugin-react'
import { defineConfig } from 'vitest/config'
export default defineConfig({
plugins: [reactNative(), react()],
})After (vitest-native):
import { reactNative } from 'vitest-native'
import { defineConfig } from 'vitest/config'
export default defineConfig({
plugins: [reactNative()],
})Two changes:
- Named import —
import { reactNative }, not a default import. - Drop
@vitejs/plugin-react— vitest-native configures the JSX runtime itself (both engines). Addingplugin-reactalongside it is unnecessary and can double-transform.
That's it. reactNative() with no options resolves to the native engine (real RN) whenever @react-native/babel-preset is installed, which it now is.
3. Run
npx vitest runYour existing tests render against real React Native exactly as before.
What's the same
- Real RN component rendering via
@testing-library/react-native. vi.mock()of your own modules (anything in your app's source graph).- Standard Vitest everything —
expect,vi.fn, coverage, watch, UI.
What's new (and optional)
- Native is the zero-config default, but a fast mock engine is one option away —
reactNative({ engine: 'mock' })— for pure-logic and environment-control tests. A CI-gated cross-check keeps the mock behaviorally honest against real RN. - Presets auto-shadow common native libraries (Reanimated, Gesture Handler, Safe Area, Navigation, …) the way
jestmocks them — no per-lib wiring. - A hot runtime, opt-in where the run can be bounded (
hotRuntime: 'auto'), keeps RN warm across files while resetting app/test modules and common process-wide pollution between files. It uses Vitest's custom worker APIs and has known differences from per-file isolation; the default,hotRuntime: false, gives each file a fresh worker.
Gotchas (the margins)
These are inherent to running RN externalized to Node — they applied to the original plugin too:
vi.mockof an externalized RN-side library may not intercept. Libraries that load through Node (not your Vite source graph) bypass Vitest's mocker. Prefer a preset (if one exists) or mock at the boundary. Your own modules mock normally.- Custom Babel plugins don't run. Transforms go through Vite/esbuild, not your
babel.config.js. Flow/TS stripping for RN and allow-listed packages is handled by the require hook; use thetransformallowlist for extra pure-JS packages that ship untranspiled source.
If something that worked under the old plugin doesn't here, open an issue — parity with vitest-react-native is a goal, and the cross-check corpus is how we prove it.