In React Native, what does a screen reader announce for an icon-only Pressable without accessibilityLabel, and how do accessibilityLabel and accessibilityHint fix it?
answer
- default label comes from Text children
- an icon has no Text to read
- Pressable sets no role
- label names purpose, not picture
- hint: after the label, optional
basics
~20 sReact 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 sA `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 linesimport { 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
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.
Explain the concatenated-text default, why Pressable has no default role, and how hints differ between VoiceOver and TalkBack.
Set labelling rules for a component library: purpose over appearance, no role words, localisation, and hints only when results are not obvious.
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