skip to content

Upgrading a React Native suite from React Native Testing Library 13 to 14, what breaks, and what do the rntl-v14 codemods fix for you?

level: seniorimportance: should knowfreq 38%

answer

  1. React 19 and React Native 0.78 floor
  2. render, fireEvent, act, renderHook now async
  3. test-renderer replaces react-test-renderer
  4. update, UNSAFE_root, UNSAFE_*ByType gone
  5. customRenderFunctions for your own helpers

basics

~10 s

RNTL 14 needs React 19 and React Native 0.78+, makes render, renderHook, fireEvent and act async, swaps react-test-renderer for test-renderer and removes update, UNSAFE_root, UNSAFE_*ByType/ByProps, concurrentRoot and createNodeMock. Codemods update dependencies and add awaits.

solid answer

~40 s

The v14 upgrade has three kinds of change. **Platform floor:** React 19+, React Native 0.78+, Node `^22.13.0 || >=24`; a React 18 app stays on v13. **Async core APIs:** `render`, `renderHook`, `fireEvent` and `act` return Promises, so every call needs `await` and every test that calls them becomes `async`; the v13.3 `renderAsync`-style names are gone. **Removals:** the `update` alias (use `rerender`), `getQueriesForElement` (use `within`), `UNSAFE_root` (use `container` or `root`), the `UNSAFE_*ByType`/`*ByProps` queries, and the `concurrentRoot`, `createNodeMock` and `unstable_validateStringsRenderedWithinText` options; strings outside `<Text>` now always throw. Two codemods do most of it: `rntl-v14-update-deps` swaps `react-test-renderer` for `test-renderer` in `package.json`, and `rntl-v14-async-functions` adds the awaits and `async` keywords, including in helpers you name with `customRenderFunctions`. Helpers defined inside test files and namespace imports still need manual work, and v14's unknown-option warnings flag stale options.

code

typescript · 13 lines
typescript
// v13
test('submits', () => {
  const { update } = render(<SurveyScreen />);
  fireEvent.press(screen.getByText('Submit'));
  update(<SurveyScreen locale="fr" />);
});

// v14
test('submits', async () => {
  const { rerender } = await render(<SurveyScreen />);
  await fireEvent.press(screen.getByText('Submit'));
  await rerender(<SurveyScreen locale="fr" />);
});

go deeper

for a junior

Recall that RNTL 14 needs await on render, fireEvent, act and renderHook, and that test functions become async.

for a middle

Explain the removed APIs and their replacements, the test-renderer swap, and what each of the two codemods changes.

for a senior

Plan the upgrade: platform first, codemods with customRenderFunctions, then manual fixes for helpers, removed queries, stale options and bare strings.

for a principal

Weigh when to take the upgrade against the React 19 and React Native 0.78 floor, and sequence it with the app's own framework upgrade.

## Why v14 is a real migration **React Native Testing Library (RNTL)** 14 drops React 18 and moves to React 19's asynchronous rendering model. For a survey app's test suite written against v13, that touches nearly every test file, which is why the maintainers ship codemods. Interviewers ask about it because it separates people who ran the upgrade from people who read the release title. ## What changed | Area | v13 | v14 | |---|---|---| | React / React Native / Node | React 18+, RN 0.71+, Node 18+ | React 19+, RN 0.78+, Node `^22.13.0 \|\| >=24` | | Renderer | `react-test-renderer` (deprecated by React) | `test-renderer` 1.x as a peer dependency | | `render`, `renderHook`, `fireEvent`, `act` | synchronous (async variants added in 13.3) | return Promises, must be awaited | | `update` | alias of `rerender` | removed | | `getQueriesForElement` | alias of `within` | removed | | `UNSAFE_root` | raw root instance | removed; use `container` or `root` | | `UNSAFE_*ByType`, `UNSAFE_*ByProps` | could return composite components | removed; the renderer exposes host elements only | | `concurrentRoot`, `createNodeMock` options | available | removed; rendering is always concurrent | | Text outside `<Text>` | validation opt-in | always throws, like React Native at runtime | Other behaviour changes that show up as test failures: - Content React keeps hidden (suspended subtrees, `<Activity mode="hidden">`) is given `display: 'none'`, so default queries skip it and `toBeVisible()` treats it as hidden. - Accessible-name calculation now prefers labels over text, uses `Image` `alt`, and joins child names with spaces, which affects `getByRole(..., { name })` and `toHaveAccessibleName()`. - Unknown options passed to `render`, `renderHook`, `configure` or `userEvent.setup` log a warning, which surfaces stale v13 options hidden in shared helpers. ## The two codemods 1. **`rntl-v14-update-deps`**, run at the project root, edits `package.json`: removes `react-test-renderer` and `@types/react-test-renderer`, adds `test-renderer` on the recommended 1.x line, and bumps `@testing-library/react-native`. Pick the `test-renderer` line that matches your React 19 minor. 2. **`rntl-v14-async-functions`**, run on the test sources, adds `await` to `render()`, `act()`, `renderHook()` and `fireEvent()` calls, handles `screen.rerender()`, `screen.unmount()` and renderer methods, and makes the enclosing test functions `async`. ```bash npx codemod@latest rntl-v14-update-deps --target . npx codemod@latest rntl-v14-async-functions --target ./src \ --param customRenderFunctions="renderWithProviders,renderSurvey" ``` Both are safe to run more than once; they only touch code not yet migrated. ## What the codemods leave to you - **Custom render helpers** are transformed only if you list them in `customRenderFunctions`; the helper itself must become `async` and `return await render(...)`. - **Helpers defined inside test files** are not transformed by default. - **Namespace imports** (`import * as RNTL from '@testing-library/react-native'`) are not handled. - **Removed APIs** have no automatic replacement: `UNSAFE_getByType(SubmitButton)` must become a query on what the user perceives, such as `getByRole('button', { name: 'Submit' })`. - **Components that render bare strings** outside `<Text>` now fail, which is a real bug to fix in the component. - **Tests of very short transient states** may stop working, since awaiting `act` flushes all pending updates before returning. ## The matcher story, one version earlier Many suites reaching v14 still carry v12-era setup. Since v13 the Jest matchers (`toBeOnTheScreen`, `toBeVisible`, `toBeDisabled`, `toHaveTextContent` and the rest) are built in and registered on import, so `@testing-library/jest-native` and its `extend-expect` import should be removed; its `toHaveAccessibilityState()` was replaced by the specific state matchers. Importing from `@testing-library/react-native/pure` skips both the automatic matcher registration and the automatic cleanup. ## Checking the result After the codemods and manual fixes, a few searches confirm nothing was missed: - `renderAsync`, `fireEventAsync`, `renderHookAsync`: should be gone, replaced by the plain names; - `UNSAFE_` and `getQueriesForElement`: should be gone; - `extend-expect` and `@testing-library/jest-native`: should be gone since v13; - any `render(`, `fireEvent.` or `act(` without `await` in shared helpers; the `await-async-utils`-style lint rules do not cover every custom helper. ## A migration order that works 1. Upgrade React and React Native first; v14 cannot run on React 18. 2. Run `rntl-v14-update-deps`, install, then `rntl-v14-async-functions` with your helper names. 3. Run the suite with warnings visible; fix unknown-option warnings and removed-API errors. 4. Review diffs in shared test utilities by hand, since that is where the codemods have the least context.

  • Why did RNTL 14 remove the UNSAFE_*ByType queries instead of keeping them deprecated?
    They returned composite components, and the `test-renderer` package v14 is built on exposes host elements only, so there is nothing for them to find. The replacement is a query on what the user perceives, such as a role and accessible name, which also makes the test less coupled to component structure.
  • After the codemods, a shared renderWithProviders helper still returns an unawaited promise in some tests. What happened?
    The codemod only transforms custom render functions you name in `customRenderFunctions`, and it does not rewrite helpers defined inside test files. Make the helper `async`, `return await render(...)`, and rerun the codemod with the helper's name so call sites get `await`.

saying these in an interview costs you the question

  • RNTL 14 works with React 18 if you add the async codemod.
  • renderAsync is the v14 name for an awaited render.
  • The codemods also rewrite UNSAFE_getByType into role queries.
  • RNTL 14 still needs @testing-library/jest-native for toBeDisabled.
  • Strings outside Text can still be allowed with a render option in v14.