RNTL v14 API Reference
Complete API reference for @testing-library/react-native v14.x (React 19+).
Test renderer: test-renderer (not react-test-renderer)
Element type: HostElement (not ReactTestInstance)
Table of Contents
- Core Pattern
- Key Rule: Always await
- render
- screen
- Queries
- User Event
- Fire Event
- Jest Matchers
- Async Utilities
- Other Helpers
- renderHook
- act
- Configuration
- Accessibility
- Removed APIs
- Migration Codemods
Core Pattern
import { render, screen, userEvent } from '@testing-library/react-native';
jest.useFakeTimers(); // recommended when using userEvent
test('description', async () => {
const user = userEvent.setup();
await render(<Component />); // async in v14 — always await
const button = screen.getByRole('button', { name: 'Submit' });
await user.press(button);
expect(screen.getByText('Done')).toBeOnTheScreen();
});Key Rule: Always await
In v14, the following APIs are async and must always be awaited:
await render(<Component />)await fireEvent.press(element)(and all fireEvent variants)await screen.rerender(<Component />)await screen.unmount()await renderHook(() => useHook())await act(() => { ... })(even with sync callbacks)
render
async function render(
component: React.Element<any>,
options?: RenderOptions,
): Promise<RenderResult>;render returns a Promise<RenderResult> — always await it.
There is no renderAsync in v14. The standard render is already async.
Options
| Option | Type | Description |
|---|---|---|
wrapper |
React.ComponentType<any> |
Wraps tested component (useful for context providers) |
createNodeMock |
(element) => unknown |
Custom mock refs |
Note: concurrentRoot and unstable_validateStringsRenderedWithinText are removed in v14 (concurrent rendering is always on, string validation is always on).
Example
import { render, screen } from '@testing-library/react-native';
await render(<MyApp />);
expect(screen.getByRole('button', { name: 'start' })).toBeOnTheScreen();
// With wrapper
await render(<MyComponent />, {
wrapper: ({ children }) => <ThemeProvider>{children}</ThemeProvider>,
});screen
let screen: {
...queries;
rerender(element: React.Element<unknown>): Promise<void>; // async
unmount(): Promise<void>; // async
debug(options?: { message?: string; mapProps?: MapPropsFunction }): void;
toJSON(): RendererJSON | null;
container: HostElement; // safe root host element
root: HostElement; // root host element
};The screen object provides queries and utilities for the currently rendered UI. Assigned after render(), cleared after each test via auto-cleanup.
Methods
rerender(element)— Re-render with new root element. Async — mustawait. Triggers lifecycle events.unmount()— Unmount the tree. Async — mustawait. Usually not needed (auto-cleanup).debug({ message?, mapProps? })— Pretty-print the rendered tree. UsemapPropsto filter/transform props in output.toJSON()— Get JSON representation (for snapshot testing).container— Root host element (safe accessor, replacesUNSAFE_root).root— Root host element.
Note: UNSAFE_root is removed in v14. Use container or root instead.
Queries
Each query = variant + predicate (e.g., getByRole = getBy + ByRole).
Query Variants
| Variant | Assertion | Return Type | Async |
|---|---|---|---|
getBy* |
Exactly one match | HostElement (throws if 0 or >1) |
No |
getAllBy* |
At least one match | HostElement[] (throws if 0) |
No |
queryBy* |
Zero or one match | HostElement | null (throws if >1) |
No |
queryAllBy* |
No assertion | HostElement[] (empty if 0) |
No |
findBy* |
Exactly one match | Promise<HostElement> |
Yes |
findAllBy* |
At least one match | Promise<HostElement[]> |
Yes |
findBy* / findAllBy* accept optional waitForOptions: { timeout?, interval?, onTimeout? }.
Query Predicates
*ByRole (preferred)
getByRole(role: TextMatch, options?: {
name?: TextMatch;
disabled?: boolean;
selected?: boolean;
checked?: boolean | 'mixed';
busy?: boolean;
expanded?: boolean;
value?: { min?: number; max?: number; now?: number; text?: TextMatch };
includeHiddenElements?: boolean;
}): HostElement;Matches elements by role or accessibilityRole. Element must be an accessibility element:
Text,TextInput,Switchare by defaultViewneedsaccessible={true}(or usePressable/TouchableOpacity)
Common roles: button, text, heading (alias: header), searchbox, switch, checkbox, radio, img, link, alert, menu, menuitem, tab, tablist, progressbar, slider, spinbutton, timer, toolbar.
screen.getByRole('button', { name: 'Submit' });
screen.getByRole('button', { name: 'Submit', disabled: true });
screen.getByRole('checkbox', { checked: true });
screen.getByRole('slider', { value: { now: 50, min: 0, max: 100 } });*ByLabelText
getByLabelText(text: TextMatch, options?: { exact?: boolean; normalizer?: Function; includeHiddenElements?: boolean }): HostElement;Matches by aria-label/accessibilityLabel or text content of element referenced by aria-labelledby/accessibilityLabelledBy.
*ByPlaceholderText
getByPlaceholderText(text: TextMatch, options?: { exact?: boolean; normalizer?: Function; includeHiddenElements?: boolean }): HostElement;Matches TextInput by placeholder prop.
*ByText
getByText(text: TextMatch, options?: { exact?: boolean; normalizer?: Function; includeHiddenElements?: boolean }): HostElement;Matches by text content. Joins <Text> siblings to find matches (like RN runtime).
*ByDisplayValue
getByDisplayValue(value: TextMatch, options?: { exact?: boolean; normalizer?: Function; includeHiddenElements?: boolean }): HostElement;Matches TextInput by current display value.
*ByHintText
getByHintText(hint: TextMatch, options?: { exact?: boolean; normalizer?: Function; includeHiddenElements?: boolean }): HostElement;Matches by accessibilityHint prop. Also available as getByA11yHint / getByAccessibilityHint.
*ByTestId (last resort)
getByTestId(testId: TextMatch, options?: { exact?: boolean; normalizer?: Function; includeHiddenElements?: boolean }): HostElement;Matches by testID prop. Use only when other queries don't work.
TextMatch
type TextMatch = string | RegExp;- String: exact full match by default. Use
{ exact: false }for case-insensitive substring match. - RegExp: substring match by default. Use anchors (
^...$) for full match.
screen.getByText('Hello World'); // exact full match
screen.getByText('llo Worl', { exact: false }); // substring, case-insensitive
screen.getByText(/World/); // regex substring
screen.getByText(/^hello world$/i); // regex full match, case-insensitiveCommon Query Options
includeHiddenElements(alias:hidden): Include elements hidden from accessibility. Default:false.exact: Defaulttrue. Whenfalse, matches substrings case-insensitively. No effect on RegExp.normalizer: Custom text normalization function. Default trims whitespace and collapses multiple spaces. UsegetDefaultNormalizer({ trim?, collapseWhitespace? })to customize.
User Event
Prefer userEvent over fireEvent. User Event simulates realistic interaction sequences on host elements only, with proper event data.
Setup
const user = userEvent.setup(options?: { delay?: number; advanceTimers?: (delay: number) => Promise<void> | void });press(element)
await user.press(element): Promise<void>;Full press lifecycle: pressIn → press → pressOut. Minimum 130ms. Use fake timers to speed up.
longPress(element, options?)
await user.longPress(element, { duration?: number }): Promise<void>;Long press (default 500ms). Emits pressIn, longPress, pressOut.
type(element, text, options?)
await user.type(textInput, text: string, options?: {
skipPress?: boolean; // skip pressIn/pressOut
skipBlur?: boolean; // skip endEditing/blur
submitEditing?: boolean; // trigger submitEditing
}): Promise<void>;Character-by-character typing on TextInput. Appends to existing text (use clear() first to replace).
Event sequence per character: keyPress → change → changeText → selectionChange (+ contentSizeChange for multiline).
clear(element)
await user.clear(textInput): Promise<void>;Clears TextInput content. Events: focus → selectionChange → keyPress → change → changeText → selectionChange → endEditing → blur.
paste(element, text)
await user.paste(textInput, text: string): Promise<void>;Pastes text into TextInput. Events: focus → selectionChange → change → changeText → selectionChange → endEditing → blur.
scrollTo(element, options)
await user.scrollTo(scrollView, options: {
y: number; momentumY?: number; // vertical scroll
// OR
x: number; momentumX?: number; // horizontal scroll
contentSize?: { width: number; height: number };
layoutMeasurement?: { width: number; height: number };
}): Promise<void>;Scrolls a host ScrollView (including FlatList). Match scroll direction to horizontal prop. Pass contentSize and layoutMeasurement for FlatList updates. Remembers last scroll position between calls.
Fire Event
Use when userEvent doesn't support the event or when triggering events on composite elements.
Async in v14 — always await:
async function fireEvent(
element: HostElement,
eventName: string,
...data: unknown[]
): Promise<void>;Traverses tree bottom-up looking for onXxx handler. Does not auto-pass event data.
Convenience Methods
await fireEvent.press(element, ...data): Promise<void>;
await fireEvent.changeText(element, ...data): Promise<void>;
await fireEvent.scroll(element, ...data): Promise<void>;There is no fireEventAsync in v14. The standard fireEvent is already async.
Jest Matchers
Available automatically with any @testing-library/react-native import. No setup needed.
Element Existence
| Matcher | Description |
|---|---|
toBeOnTheScreen() |
Element is attached to the element tree |
Element Content
| Matcher | Signature | Description |
|---|---|---|
toHaveTextContent() |
(text: string | RegExp, options?: { exact?, normalizer? }) |
Text content match |
toContainElement() |
(element: HostElement | null) |
Contains child element |
toBeEmptyElement() |
— | No children or text content |
Element State
| Matcher | Description |
|---|---|
toHaveDisplayValue(value: string | RegExp, options?) |
TextInput display value |
toHaveAccessibilityValue({ min?, max?, now?, text? }) |
Accessibility value (partial match) |
toBeEnabled() / toBeDisabled() |
Disabled state via aria-disabled (checks ancestors) |
toBeSelected() |
Selected state via aria-selected |
toBeChecked() / toBePartiallyChecked() |
Checked state via aria-checked |
toBeExpanded() / toBeCollapsed() |
Expanded state via aria-expanded |
toBeBusy() |
Busy state via aria-busy |
Element Style
| Matcher | Description |
|---|---|
toBeVisible() |
Not hidden (display: none, opacity: 0, or hidden from a11y) |
toHaveStyle(style: StyleProp<Style>) |
Specific style match |
Other
| Matcher | Signature | Description |
|---|---|---|
toHaveAccessibleName() |
(name?: string | RegExp, options?: { exact?, normalizer? }) |
Accessible name from aria-label/aria-labelledby or text content |
toHaveProp() |
(name: string, value?: unknown) |
Prop existence/value check (last resort) |
Async Utilities
waitFor
function waitFor<T>(
expectation: () => T,
options?: { timeout?: number; interval?: number },
): Promise<T>;Runs expectation every interval (default 50ms) until timeout (default 1000ms). Callback must throw on failure. Auto-detects and works with Jest fake timers.
Rules:
- No side effects inside callback (no
fireEvent/userEvent) - One assertion per
waitFor - Never pass empty callback
- Prefer
findBy*overwaitFor+getBy*
waitForElementToBeRemoved
function waitForElementToBeRemoved<T>(
expectation: () => T,
options?: { timeout?: number; interval?: number },
): Promise<T>;Waits until the queried element is removed. Element must be initially present.
Other Helpers
within
function within(element: HostElement): Queries;Scoped queries on a subtree. Useful for querying within a single FlatList item or a specific screen.
const item = within(screen.getByTestId('list-item-1'));
expect(item.getByText('Title')).toBeOnTheScreen();cleanup
function cleanup(): void;Unmounts rendered trees and clears screen. Automatic after each test (if test runner supports afterEach). Don't call manually unless using @testing-library/react-native/pure.
renderHook
Async in v14:
async function renderHook<Result, Props>(
hookFn: (props?: Props) => Result,
options?: { initialProps?: Props; wrapper?: React.ComponentType },
): Promise<{
result: { current: Result };
rerender: (props: Props) => Promise<void>;
unmount: () => Promise<void>;
}>;There is no renderHookAsync in v14. The standard renderHook is already async.
Renders a test component that calls the provided hook. Use act() when calling functions returned by the hook that trigger state updates.
const { result } = await renderHook(() => useCount());
expect(result.current.count).toBe(0);
await act(() => {
result.current.increment();
});
expect(result.current.count).toBe(1);
// With wrapper
await renderHook(() => useHook(), {
wrapper: ({ children }) => <Provider>{children}</Provider>,
});act
In v14, act always returns a Promise<T> and must be awaited, even with sync callbacks:
async function act<T>(callback: () => T): Promise<T>;await act(() => {
result.current.increment();
});Usually not needed — render, fireEvent, userEvent, and waitFor handle it internally.
Configuration
function configure(
options: Partial<{
asyncUtilTimeout: number; // default timeout for waitFor/findBy* (default: 1000ms)
defaultIncludeHiddenElements: boolean; // default for includeHiddenElements option (default: false)
defaultDebugOptions: Partial<DebugOptions>;
}>,
): void;
function resetToDefaults(): void;Note: concurrentRoot option is removed (always on). unstable_validateStringsRenderedWithinText is removed (always on).
Environment Variables
| Variable | Description |
|---|---|
RNTL_SKIP_AUTO_CLEANUP=true |
Disable automatic cleanup after each test |
RNTL_SKIP_AUTO_DETECT_FAKE_TIMERS=true |
Disable auto-detection of fake timers |
Accessibility
isHiddenFromAccessibility
function isHiddenFromAccessibility(element: HostElement | null): boolean;Also available as isInaccessible() alias.
Element is hidden when it or any ancestor has:
display: nonestylearia-hidden={true}accessibilityElementsHidden={true}(iOS)importantForAccessibility="no-hide-descendants"(Android)- Sibling with
aria-modal={true}oraccessibilityViewIsModal={true}(iOS)
Removed APIs
The following are removed in v14 and must not be used:
renderAsync— Userender(already async)fireEventAsync— UsefireEvent(already async)renderHookAsync— UserenderHook(already async)rerenderAsync/unmountAsync— Usererender/unmount(already async)update()— Usererender()getQueriesForElement()— Usewithin()UNSAFE_root— UsecontainerorrootUNSAFE_getByType,UNSAFE_getAllByType,UNSAFE_queryByType,UNSAFE_queryAllByType— RemovedUNSAFE_getByProps,UNSAFE_getAllByProps,UNSAFE_queryByProps,UNSAFE_queryAllByProps— RemovedconcurrentRootoption — Always onunstable_validateStringsRenderedWithinTextoption — Always on
Migration Codemods
Use RNTL codemods to automate migration from v13:
rntl-v14-update-deps— Updatesreact-test-renderertotest-rendererinpackage.jsonrntl-v14-async-functions— Addsawaittorender,fireEvent,rerender,unmount,renderHook, andactcalls