In a React Native 0.87 app, what does @react-native/jest-preset configure, and why can't plain Jest run React Native code?
answer
- Flow, JSX and TypeScript need compiling
- node_modules allowlist for react-native
- image requires become testUri stubs
- setup file: __DEV__ plus core mocks
- separate package since 0.85
basics
~10 s@react-native/jest-preset makes Jest able to load React Native code: it compiles JS and TS through babel-jest, transforms React Native's own untranspiled packages, stubs image requires, uses a Node-based environment and mocks core native modules.
solid answer
~40 sReact Native ships its JavaScript as untranspiled source (Flow types, JSX, ES modules) and expects native modules that only exist on a device, so plain Jest either fails to parse it or crashes calling native code. The preset, a separate package `@react-native/jest-preset` since 0.85, fixes both: `transform` runs `.js`, `.ts` and `.tsx` through `babel-jest` and image files through a stub transformer; `transformIgnorePatterns` lets `react-native` and `@react-native/*` packages be compiled; `setupFiles` loads a script that defines `__DEV__`, `requestAnimationFrame` and friends and mocks core modules such as `Linking`, `AppState` and `NativeModules`; `testEnvironment` is its Node-based `react-native-env`. In 0.87 you install it and set `preset: '@react-native/jest-preset'`.
code
javascript · 4 lines// jest.config.js
module.exports = {
preset: '@react-native/jest-preset',
};go deeper
Recall that React Native code needs compiling and native mocks before Jest can run it, and that the preset is the package @react-native/jest-preset set in jest.config.js.
Walk through the preset's keys: babel-jest transform, the node_modules allowlist in transformIgnorePatterns, the setup file with globals and core mocks, and the Node-based react-native-env.
Use the preset's boundaries to triage failures: parse errors mean an untranspiled dependency, crashes in native calls mean a missing mock, device-only behaviour means the test is at the wrong layer.
Treat the preset as a pinned dependency that moves with each React Native upgrade, and decide how much native behaviour a team may trust from Node-side tests before paying for device runs.
## Why plain Jest cannot run React Native code Jest is a test runner for JavaScript that executes in **Node.js**. A React Native app is written for a very different host: Metro bundles it, Hermes runs it on the phone, and the framework's components talk to native iOS and Android code. Two things break when you point an unconfigured Jest at a React Native project: 1. **Syntax.** The `react-native` package and many `@react-native/*` packages are published as source that still contains Flow type annotations, JSX and `import`/`export` statements. Node's module loader inside Jest cannot parse that, and Jest by default does **not** transform anything under `node_modules`. 2. **Native dependencies.** Core modules such as `Linking`, `AppState`, `Vibration` or the view manager expect a native implementation. In Node there is no device, so calling them without a mock throws. The **Jest preset** is a ready-made bundle of Jest configuration that solves both problems so that a component test can simply `import { View } from 'react-native'`. ## What the preset configures The preset is published as **`@react-native/jest-preset`**. React Native 0.85 moved it out of the `react-native` package, and the 0.87 release notes say it must now be consumed as that package, so `preset: 'react-native'` no longer resolves. Its `jest-preset.js` sets these keys: | Key | Value in the preset | Why it matters | |---|---|---| | `transform` | `.js`, `.ts`, `.tsx` through `babel-jest`; image and video files through an asset transformer | Your code and React Native's source get compiled with your Babel config | | `transformIgnorePatterns` | `node_modules/(?!((jest-)?react-native\|@react-native(-community)?)/)` | Everything in `node_modules` is skipped **except** `react-native`, `jest-react-native` and the `@react-native/` and `@react-native-community/` scopes | | `setupFiles` | the preset's `jest/setup.js` | Globals and core mocks exist before any test file loads | | `testEnvironment` | `jest/react-native-env.js` | A Node environment with React Native export conditions, not a browser DOM | | `haste` | `defaultPlatform: 'ios'`, platforms `android`, `ios`, `native` | Platform-specific files such as `Button.ios.tsx` resolve in tests | | `moduleNameMapper` and `resolver` | map `react-native` subpaths to the installed package | Deep paths such as the `Libraries/...` modules can still be resolved and mocked | ## What the setup file does The file listed in `setupFiles` runs once per test file, before the test code. It: - sets `__DEV__` to `true` and `IS_REACT_ACT_ENVIRONMENT` to `true`; - defines `requestAnimationFrame` on top of `setTimeout` and `performance.now` as a Jest mock of `Date.now`; - makes `window` an alias of the Node global object; - registers mocks for core modules and components, among them `AppState`, `AccessibilityInfo`, `Clipboard`, `Image`, `Linking`, `Modal`, `ScrollView`, `Text`, `TextInput`, `View`, `UIManager`, `NativeModules` and `Vibration`. The asset transformer turns `require('./logo.png')` into an object with a `testUri` field holding the file's relative path, so snapshots show which image a component asked for instead of failing on binary data. ## Installing it in React Native 0.87 1. Install the package as a dev dependency at the same version as `react-native` (the package is versioned in lockstep, for example 0.87.1). 2. Point Jest at it in `jest.config.js` with `preset: '@react-native/jest-preset'`. 3. Keep a `babel.config.js` that uses `@react-native/babel-preset`, because `babel-jest` reads the project's Babel configuration. 4. Add your own options on top: extra `setupFiles`, a widened `transformIgnorePatterns`, `setupFilesAfterEnv` for matcher libraries. Projects created with Expo usually use the `jest-expo` preset instead, which Expo's docs describe as mocking the native part of the Expo SDK and handling most of the configuration an Expo project needs. ## What the preset does not do - It does **not** run your code on a device or simulator: rendering happens in Node, so real layout, gestures and native behaviour are not exercised. - Its native mocks cover **core** React Native modules. Third-party native libraries bring their own Jest mocks or need one written for them. - It does **not** type-check TypeScript: Babel strips types and moves on. - It does not choose a component testing library; React Native Testing Library is added separately. Knowing this boundary is what lets you explain a failing test: a syntax error in a dependency points at `transformIgnorePatterns`, a crash inside a native call points at a missing mock, and a behaviour that only shows on a phone belongs to a device-level test.
- In a React Native project with Button.ios.tsx and Button.android.tsx, which file does a Jest test import under the preset?The preset's `haste` block sets `defaultPlatform: 'ios'` with the platforms `android`, `ios` and `native`, so an import of `./Button` resolves `Button.ios.tsx`. Testing the Android variant needs its own configuration, for example a second Jest project with a different default platform, or importing the Android file directly.
- Does an Expo project use @react-native/jest-preset directly?Usually not. Expo documents the `jest-expo` preset, which builds a Jest configuration for Expo projects and mocks the native part of the Expo SDK. You still widen `transformIgnorePatterns` the same way when a dependency ships untranspiled code.
- Why does the preset map image requires to an object with testUri instead of a number?In a Metro bundle an image `require` returns an asset registry id, but Jest has no asset registry. The preset's asset transformer replaces the file with `{ testUri: '<relative path>' }`, so the component still receives a value and snapshots record which image was requested.
The preset is a travel adapter kit for running phone code on a laptop: a plug converter for the syntax (Babel), a list of which bags get unpacked (the node_modules allowlist) and stand-in appliances for the sockets that do not exist here (core native mocks).
saying these in an interview costs you the question
- The preset runs the tests on an iOS simulator or Android emulator
- The preset mocks every third-party native module automatically
- In React Native 0.87 you still write preset: 'react-native'
- babel-jest in the preset type-checks the TypeScript tests
- The preset renders components into a jsdom document