skip to content

Assistive Tech Support

React Native maps accessibility props onto VoiceOver and TalkBack, and AccessibilityInfo reads reader and motion settings. Interviewers check whether the app works for users who cannot see it.

part ofReact Nativeoverview, primer and where to startread it →
on this pageshow

explore

questions

19

In React Native, what does a screen reader announce for an icon-only Pressable without accessibilityLabel, and how do accessibilityLabel and accessibilityHint fix it?

level: juniorimportance: must knowfreq 68%

answer

  1. default label comes from Text children
  2. an icon has no Text to read
  3. Pressable sets no role
  4. label names purpose, not picture
  5. hint: after the label, optional

basics

~20 s

React Native builds a default label from a pressable's Text children, so an icon-only Pressable has no meaningful name and no role. Set accessibilityLabel (or aria-label) to its purpose and role='button'; use accessibilityHint only to explain a non-obvious result.

solid answer

~50 s

A `Pressable` is accessible by default, so VoiceOver and TalkBack can focus it, but its spoken name is built by concatenating its `Text` children. An icon-only button — an `Image` or an icon component — has no text, so the user lands on an element with no useful name, and because `Pressable` sets no role, nothing says it is a button either. The fix is `accessibilityLabel` (or its alias `aria-label`) describing the **purpose** — "Save to favourites", not "heart icon" — plus `role="button"`. `accessibilityHint` adds a sentence about the result when the label alone is not enough, such as "Adds this restaurant to your saved list". The hint is read after the label — on iOS only if the user keeps VoiceOver hints on, while React Native's docs note hints cannot be turned off on Android. There is no `aria-*` alias for the hint. Keep labels short and never repeat the role in them.

code

tsx · 18 lines
tsx
import { Image, Pressable, StyleSheet } from 'react-native';

type Props = { onToggle: () => void };

export function SaveRestaurantButton({ onToggle }: Props) {
  return (
    <Pressable
      onPress={onToggle}
      role="button"
      aria-label="Save to favourites"
      accessibilityHint="Adds this restaurant to your saved list"
    >
      <Image source={require('./heart.png')} style={styles.icon} />
    </Pressable>
  );
}

const styles = StyleSheet.create({ icon: { width: 24, height: 24 } });

go deeper

for a junior

Recall that icon-only pressables need an accessibilityLabel or aria-label that names their purpose, plus a role, because there is no text to read.

for a middle

Explain the concatenated-text default, why Pressable has no default role, and how hints differ between VoiceOver and TalkBack.

for a senior

Set labelling rules for a component library: purpose over appearance, no role words, localisation, and hints only when results are not obvious.

for a principal

Build labelling into the design process so every icon control ships with a reviewed name, instead of relying on audits to catch nameless buttons.

## What the screen reader needs A screen reader such as **VoiceOver** (iOS) or **TalkBack** (Android) describes the focused element by combining its **name**, its **role** and its **state**. In React Native those come from props on the host component: | Piece | Prop | Alias | | --- | --- | --- | | Name | `accessibilityLabel` | `aria-label` | | Extra explanation | `accessibilityHint` | none | | Role | `accessibilityRole` | `role` | | State | `accessibilityState` | `aria-checked`, `aria-disabled`, ... | This question is about the first two; roles and states have their own rules. ## Why an icon-only button is silent `Pressable` sets `accessible` to `true` unless you pass `false`, so the element is **focusable** by assistive technology. When no label is given, React Native's documented default is to build one by **concatenating the text of all `Text` children**, separated by spaces. That works for a button with a visible caption and fails for: - a heart `Image` for "favourite"; - a vector-icon component that draws a glyph; - a close button that renders an "×" graphic. Nothing in those trees is text, so there is **no meaningful name** to read. `Pressable` also sets **no role**, so the user is not even told it can be activated. The result is a focus stop that tells a blind user nothing about what it does. ## The fix ```tsx <Pressable onPress={toggleSaved} role="button" accessibilityLabel="Save to favourites" accessibilityHint="Adds this restaurant to your saved list" > <Image source={heartIcon} style={styles.icon} /> </Pressable> ``` 1. **Label the purpose, not the picture.** "Save to favourites", not "heart". The label replaces the concatenated text entirely. 2. **Do not put the role in the label.** "Save button" is read as "Save button, button". 3. **Keep it short and specific.** "Delete review", not "Tap here to delete this review from the list". 4. **Add a role** so the user knows it is actionable. 5. **Use a hint sparingly**, for results that are not obvious from the label. ## How hints behave per platform | | iOS (VoiceOver) | Android (TalkBack) | | --- | --- | --- | | When read | After the label | After the label | | Can the user turn it off? | Yes, in VoiceOver settings | Not at this time, per React Native's docs | | Alias | none | none | Because Android users cannot silence hints, a hint that repeats the label is pure noise there. Because iOS users often turn hints off, critical information must be in the label, never only in the hint. ## Labels on text that is already visible When a button has visible text, the concatenated default is often fine. Override it only when the visible text is ambiguous out of context: three "Edit" buttons on one review screen become "Edit review title", "Edit rating", "Edit photos". On Android, `accessibilityLabelledBy` (alias `aria-labelledby`) can point an input at the `nativeID` of a visible label instead. ## Images and inputs - **Meaningful images** that are not buttons — a restaurant's photo with a caption users need — can be made accessible with `accessible`, `role="img"` and a label that says what the photo shows. - **Decorative images** should not become focus stops at all; hiding them is a grouping decision rather than a labelling one. - **Text inputs** need a name too: an `accessibilityLabel`, or on Android `accessibilityLabelledBy` / `aria-labelledby` pointing at the visible label's `nativeID`. ## Checking your labels 1. Turn on VoiceOver or TalkBack and swipe through the screen from top to bottom. 2. Write down what each focus stop says. 3. Flag every stop with no name, a name that describes a picture, or the role repeated in the name. 4. Repeat with a second language if the app is localised; untranslated labels are common. ## Common mistakes - Labelling the **inner** `Image` instead of the `Pressable` that receives focus. - Translating the visible text but leaving hard-coded English labels. - Labels that describe state ("Saved") instead of purpose; state belongs in `accessibilityState`. ## Interview summary Say that the default label is concatenated child text, so icon-only controls are nameless and role-less; fix them with a purpose-describing `accessibilityLabel` or `aria-label` and `role="button"`, and treat `accessibilityHint` as optional, platform-dependent extra detail.

  • In React Native, why is it wrong to put the word 'button' inside accessibilityLabel?
    The screen reader already announces the role from `role` or `accessibilityRole`, so a label like "Save button" is read with the role appended again. The label should name the purpose only; the role prop supplies the type.
  • In React Native, why must essential information never live only in accessibilityHint?
    On iOS the hint is read only if the user keeps VoiceOver hints enabled, and many users turn them off. Anything needed to use the control belongs in the label; the hint is for optional extra context about the result.
  • In a React Native Pressable, what happens to the visible Text when you set accessibilityLabel?
    The label replaces the concatenated child text as the spoken name. The visible text is not read in addition, so the label must carry everything the visible text said, plus whatever disambiguation you added.

saying these in an interview costs you the question

  • The screen reader reads the icon's file name, so no label is needed
  • Pressable is announced as a button by default
  • The label should describe what the icon looks like
  • Hints can be relied on because every user hears them
  • accessibilityHint has an aria-describedby alias in React Native
open as a page

In React Native, how does Text react to the system font size setting, and what do allowFontScaling and maxFontSizeMultiplier change?

level: juniorimportance: must knowfreq 48%

basics

~10 s

Text scales with the user's system font size by default because allowFontScaling is true; maxFontSizeMultiplier caps how far one Text, and the Text nested in it, may grow, while allowFontScaling={false} switches scaling off entirely.

open as a page

In React Native, how do announceForAccessibility and accessibilityLiveRegion differ for telling screen-reader users a money transfer completed?

level: middleimportance: must knowfreq 48%

basics

~20 s

AccessibilityInfo.announceForAccessibility speaks a string on both iOS and Android at the moment you call it; accessibilityLiveRegion makes TalkBack read a view when its content changes but does nothing on iOS, so VoiceOver users still need the imperative call.

open as a page

In React Native, what is the difference between accessibilityState and accessibilityValue, and how does a disabled Pressable get announced as disabled?

level: middleimportance: must knowfreq 52%

basics

~20 s

accessibilityState carries flags — disabled, selected, checked (true, false or 'mixed'), busy, expanded — while accessibilityValue carries a range (min, max, now) or a text value. Pressable copies its disabled prop into accessibilityState.disabled, so it is announced as disabled.

open as a page

In React Native, how do you make a swipe-to-archive email row usable for VoiceOver and TalkBack users who cannot perform the swipe?

level: seniorimportance: must knowfreq 40%

basics

~10 s

Declare the swipe operations as custom actions with accessibilityActions, such as archive with a label, and run them in onAccessibilityAction by switching on event.nativeEvent.actionName; screen-reader users then pick Archive from the row's action list.

open as a page

A React Native boarding-pass screen clips and overlaps at the largest system text size; how do you find and fix the causes?

level: seniorimportance: must knowfreq 40%

basics

~20 s

Reproduce it on devices at the largest text setting, then remove the fixed constraints: minHeight instead of height, no truncation of essential data, rows that wrap or stack, a scrolling screen, and caps only on already-large display text.

open as a page

In React Native, what does accessible={true} on a View do to the elements inside it for VoiceOver and TalkBack?

level: juniorimportance: should knowfreq 42%

basics

~20 s

It turns the View into a single accessibility element, so the screen reader focuses the group once and reads its children together instead of stopping on each child; Pressable and the other touchables are already accessible by default.

open as a page

In React Native, how do you check whether a screen reader or reduce motion is on, and keep that value current while the app runs?

level: juniorimportance: should knowfreq 38%

basics

~10 s

Call AccessibilityInfo.isScreenReaderEnabled() or isReduceMotionEnabled(), which return promises, for the current value, and subscribe with addEventListener to screenReaderChanged or reduceMotionChanged for later changes, removing the subscription on unmount.

open as a page

In React Native, how do you stop VoiceOver and TalkBack from reaching the inbox behind a custom bottom sheet drawn in the same view tree?

level: middleimportance: should knowfreq 25%

basics

~10 s

Hide the background: aria-hidden={true} on the inbox container sets accessibilityElementsHidden on iOS and importantForAccessibility no-hide-descendants on Android; on iOS, accessibilityViewIsModal on the sheet also works, but only against the sheet's siblings.

open as a page

In React Native's AccessibilityInfo, what happens when code queries or subscribes to a setting the current platform does not support?

level: middleimportance: should knowfreq 20%

basics

~10 s

Mostly nothing visible: an iOS-only query such as isBoldTextEnabled resolves to false on Android, an unmapped event returns an inert subscription, and isAccessibilityServiceEnabled rejects on iOS, so false often means unsupported rather than off.

open as a page

In React Native, how do the role and aria-* props relate to accessibilityRole, accessibilityLabel, accessibilityState and accessibilityValue, and which wins when both are set?

level: middleimportance: should knowfreq 38%

basics

~20 s

role and the aria-* props are aliases with web-style names for React Native's accessibility props. role takes precedence over accessibilityRole, aria-label over accessibilityLabel, and each aria state or value prop overrides only its own field, merging with the rest.

open as a page

In React Native, what does adjustsFontSizeToFit do, and when is it acceptable for text that grows too large?

level: middleimportance: should knowfreq 25%

basics

~20 s

adjustsFontSizeToFit shrinks a Text until it fits its constraints, such as numberOfLines and the available width, down to minimumFontScale; because it undoes the user's larger text, it suits only short labels in fixed space, never body text.

open as a page

In React Native, how do you keep a boarding pass's buttons at least 44pt on iOS and 48dp on Android as text grows?

level: middleimportance: should knowfreq 30%

basics

~20 s

Give the pressable area minHeight and minWidth of at least 48, which React Native's density-independent units make at least 44pt on iOS and 48dp on Android, pad small icons, and never fix the height, so targets grow with their text.

open as a page

In React Native, what decides the order VoiceOver and TalkBack move through an email row, and how do you change it when it reads wrong?

level: seniorimportance: should knowfreq 22%

basics

~20 s

By default the order comes from the native view hierarchy and layout, so fix it by ordering the JSX the way it should be read, grouping the row with an explicit label, or, experimentally, listing nativeIDs in experimental_accessibilityOrder.

open as a page

A React Native restaurant-review app renders its star rating as five unlabeled Pressables; how would you describe it to VoiceOver and TalkBack with role, label, state and value?

level: seniorimportance: should knowfreq 32%

basics

~20 s

Five unlabeled Pressables are five nameless focus stops that never speak the rating. Expose either a radiogroup of labelled radios with checked state, or one slider-role element whose accessibilityValue text reads '3 of 5 stars'.

open as a page

In React Native, what do onMagicTap and onAccessibilityEscape do, and where would you use them in an email app?

level: middleimportance: nice to knowfreq 12%

basics

~20 s

They are iOS-only VoiceOver gesture handlers: onMagicTap runs on a two-finger double-tap for the screen's most important action, such as sending a draft, and onAccessibilityEscape runs on a two-finger scrub to go back or dismiss, such as closing the compose sheet.

open as a page

In React Native, what is the iOS large content viewer, and when would you enable it with accessibilityShowsLargeContentViewer?

level: middleimportance: nice to knowfreq 12%

basics

~20 s

The iOS large content viewer shows an enlarged copy of an element's content on long press; you enable it with accessibilityShowsLargeContentViewer, optionally titled by accessibilityLargeContentTitle, for controls such as tab bar items whose text you had to cap.

open as a page

In React Native, how do you move VoiceOver or TalkBack focus to a transfer result heading, and why might focus not land there?

level: seniorimportance: nice to knowfreq 22%

basics

~20 s

Call AccessibilityInfo.sendAccessibilityEvent with the heading's host instance from a ref and the 'focus' event type, after the heading has rendered; focus fails to land when the ref is null, the target is not accessible, or the platform moves focus afterwards.

open as a page