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)
- 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
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();
});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)
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.
| 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).
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>,
});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.
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.
Each query = variant + predicate (e.g., getByRole = getBy + ByRole).
| 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? }.
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 } });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.
getByPlaceholderText(text: TextMatch, options?: { exact?: boolean; normalizer?: Function; includeHiddenElements?: boolean }): HostElement;Matches TextInput by placeholder prop.
getByText(text: TextMatch, options?: { exact?: boolean; normalizer?: Function; includeHiddenElements?: boolean }): HostElement;Matches by text content. Joins <Text> siblings to find matches (like RN runtime).
getByDisplayValue(value: TextMatch, options?: { exact?: boolean; normalizer?: Function; includeHiddenElements?: boolean }): HostElement;Matches TextInput by current display value.
getByHintText(hint: TextMatch, options?: { exact?: boolean; normalizer?: Function; includeHiddenElements?: boolean }): HostElement;Matches by accessibilityHint prop. Also available as getByA11yHint / getByAccessibilityHint.
getByTestId(testId: TextMatch, options?: { exact?: boolean; normalizer?: Function; includeHiddenElements?: boolean }): HostElement;Matches by testID prop. Use only when other queries don't work.
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-insensitiveincludeHiddenElements(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.
Prefer userEvent over fireEvent. User Event simulates realistic interaction sequences on host elements only, with proper event data.
const user = userEvent.setup(options?: { delay?: number; advanceTimers?: (delay: number) => Promise<void> | void });await user.press(element): Promise<void>;Full press lifecycle: pressIn → press → pressOut. Minimum 130ms. Use fake timers to speed up.
await user.longPress(element, { duration?: number }): Promise<void>;Long press (default 500ms). Emits pressIn, longPress, pressOut.
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).
await user.clear(textInput): Promise<void>;Clears TextInput content. Events: focus → selectionChange → keyPress → change → changeText → selectionChange → endEditing → blur.
await user.paste(textInput, text: string): Promise<void>;Pastes text into TextInput. Events: focus → selectionChange → change → changeText → selectionChange → endEditing → blur.
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.
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.
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.
Available automatically with any @testing-library/react-native import. No setup needed.
| Matcher | Description |
|---|---|
toBeOnTheScreen() |
Element is attached to the element tree |
| 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 |
| 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 |
| Matcher | Description |
|---|---|
toBeVisible() |
Not hidden (display: none, opacity: 0, or hidden from a11y) |
toHaveStyle(style: StyleProp<Style>) |
Specific style match |
| 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) |
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*
function waitForElementToBeRemoved<T>(
expectation: () => T,
options?: { timeout?: number; interval?: number },
): Promise<T>;Waits until the queried element is removed. Element must be initially present.
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();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.
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>,
});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.
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).
| 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 |
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)
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
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