Skip to content

vitest-nativeTest React Native with Vitest.

Run your tests against real React Native — the same JavaScript that ships in your app — mocking only the native-module boundary. One plugin, two engines, zero config.

vitest-native

Zero config to first test

ts
// vitest.config.ts
import { defineConfig } from 'vitest/config'
import { reactNative } from 'vitest-native'

export default defineConfig({
  plugins: [reactNative()],
})
tsx
// MyComponent.test.tsx
import { describe, it, expect } from 'vitest'
import { render, screen } from '@testing-library/react-native'
import { MyComponent } from './MyComponent'

describe('MyComponent', () => {
  it('renders correctly', () => {
    render(<MyComponent title="Hello" />)
    expect(screen.getByText('Hello')).toBeTruthy()
  })
})

reactNative() with no options resolves to the native engine — real React Native JS, mocking only the native boundary — whenever your project's Babel preset is present (i.e. any real RN app). No further setup.

Two engines, one plugin

vitest-native lets you choose the fidelity each suite needs — real React Native, or a fast mock — from one plugin:

engine: 'native' (default)engine: 'mock'
What runsReal React Native JSFast pure-JS reimplementation
MocksOnly the native boundaryAll of React Native
Best forFidelity, integration, accessibility, avoiding mock driftPure-logic suites, environment control, max determinism
BabelNeeds @react-native/babel-presetNone — just Vite

Both engines share the same test API (RNTL, the helpers, the presets). A CI-gated cross-check keeps the mock behaviorally honest against real RN.

How it compares to Jest

Jest with @react-native/jest-preset is the React Native standard and works well. Reach for vitest-native when you value:

  • Higher-fidelity option — Jest's RN preset and vitest-native both run real RN JS and mock the native side, but Jest mocks more (it swaps RN's core components and a few APIs for passthrough stand-ins). vitest-native's engine: 'native' mocks only the deeper native boundary, so your tests run RN's real component JS — or drop to a fast full mock when you don't need that. How the boundaries differ →
  • DX — Vitest's watch mode, UI, and native ESM tooling.
  • Unification — one runner if you also test web or server code with Vitest.

It is not primarily a speed play — choose it for the fidelity option and DX. Read the full comparison →

How it's verified

The reproducible guarantee is a CI-gated behavioral cross-check: 56 probes run the same assertions under the mock engine and real React Native across React Native 0.81–0.85, and any divergence fails the build. Anyone can run it (bun run crosscheck). On top of that, the full CI gate runs lint, typecheck, build, and the mock + native + hot suites across an OS × Node matrix.

We've also run real apps' own test suites under the native engine. react-native-paper's suite passes 625 of 734 tests (~85%) — no paper source changed, just an RNTL bump and the test config/setup. The remaining failures are tests coupled to Jest's RN-mock internals (e.g. vi.spyOn(View.prototype, 'measure'), deep jest.mock('react-native/…') of Appearance/Dimensions), not vitest-native bugs. The Expo-based obytes template runs 34 of 40 (~85%) — a more deeply-coupled case that needed a few library mocks. Both are reproducible: see vitest-native-bakeoffs. We've also migrated a Rocket.Chat suite in local testing.

Beta. Some APIs may still shift before 1.0. Maintained successor to vitest-community/vitest-react-native — same core idea, rebuilt for modern Vitest (v4).

Get started →

Released under the MIT License.