Skip to content

A Vitest plugin for React Native and ExpoTest React Native as it ships.

Your components run React Native's real JavaScript, and only its native side is mocked.

npm i -D vitest vite vitest-native @testing-library/react-native

Pre-1.0React Native 0.86–0.88Expo SDK 57 and 58Vitest 5Node 22.22.3+, 24.12+ or 26+

Counter.test.tsx
tsx
import { beforeEach, expect, it } from 'vitest'
import { fireEvent, render, screen } from '@testing-library/react-native'
import { Counter } from './Counter'

beforeEach(async () => { await render(<Counter />) })
it('is a button', () => {
  expect(screen.getByRole('button')).toBeOnTheScreen()
})
it('starts at zero', () => {
  expect(screen.getByText('Count: 0')).toBeOnTheScreen()
})
it('counts a press', async () => {
  await fireEvent.press(screen.getByRole('button'))
  expect(screen.getByText('Count: 1')).toBeOnTheScreen()
})
npx vitest run
✓ src/Counter.test.tsx > is a button
✓ src/Counter.test.tsx > starts at zero
✓ src/Counter.test.tsx > counts a press
Test Files 1 passed (1)
Tests 3 passed (3)

Where to start

I'm on Jest today

Find out what changes on your own suite before you switch anything.

  1. Compare · see what differs on your suite
    npx vitest-native compare
  2. Migrate · write the Vitest config
    npx vitest-native migrate --write
Migrate from Jest

New project

Set up a bare React Native or Expo app and run your first test.

  1. Set up · write the Vitest config
    npx vitest-native init
  2. Run · in watch mode
    npx vitest
Start a project

Library author

Pick the engine by what your library does.

Renders components
engine: 'native'
Logic only
engine: 'mock'

Either way, the same tests and the same Testing Library API.

Choose an engine

What runs in your tests ​

Jest's React Native preset also runs React Native's JavaScript, but it swaps the core components for stand-ins. vitest-native mocks a layer lower, so View, Text and ScrollView run as they ship.

Layer
Your componentsthe code you shiprunsrunsruns
React Native componentsView, Text, ScrollView, ImageMock boundary above. stand-in1runs2Mock boundary above. reimplemented
React Native JavaScriptStyleSheet, Platform, Animatedruns3runsreimplemented
Libraries with native codeReanimated, Gesture Handlermocked4Mock boundary above. mocked4mocked4
Native sidenative modules, host componentsmockedmockedreimplemented
A rendered <View>the host it shows up as<View><RCTView><View>
  • runs as it ships
  • stand-in or mocked
  • reimplemented
  • mock boundary
  1. 1 View, Text, ScrollView, TextInput, Image, Modal and a few others render as flat stand-ins named after the component.
  2. 2 Except TextInput, a passthrough as in Jest’s preset, so onChangeText fires once per keystroke.
  3. 3 Except a few APIs, such as useColorScheme, Vibration, Linking, AppState and Clipboard, which are mocked.
  4. 4 Each runs as its own Jest mock or setup, or as a preset. Under Jest you add these yourself; vitest-native applies them to the libraries it finds installed.
Each column is one way to run the same test. For React Native itself, the native engine draws the line lowest: only its native side is mocked. The details

A second engine, engine: 'mock', runs without React Native at all, for pure-logic suites. Choosing an engine →

A first test ​

npx vitest-native init writes the config, or add the plugin yourself:

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

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

it('renders the name', async () => {
  await render(<Greeting name="Ada" />)
  expect(screen.getByText('Hello, Ada!')).toBeTruthy()
})

Run it with npx vitest. The plugin sets up React Native Testing Library, assets and platform files, so there is no setupFiles, Jest preset or transformIgnorePatterns to write. Quick start →

How it's checked ​

  • Against real React Native. The same probes run under the mock engine and under real React Native on every supported release, and any difference fails CI. Fidelity report →
  • Against known bugs. Each bug in the project's bug museum is a runnable reproduction that fails on a build that has the bug.
  • Against your own suite. npx vitest-native compare runs your Jest and your Vitest, and lists every test that passes under Jest and not under Vitest. compare →

Pinned public apps are run too, as observations rather than evidence of capability. Validation model →

When to consider alternatives ​

  • Your Jest suite is fast and you're happy with it. vitest-native is not mainly a speed play. The reasons to switch are what runs in your tests and Vitest's tooling. Comparison →
  • A large, Jest-coupled suite moves in steps, not at once. compare shows how far there is to go, and both runners can run side by side. Migrating from Jest →
  • It's pre-1.0. Some options may still change before 1.0.

Maintained successor to vitest-community/vitest-react-native.

Released under the MIT License.