What are Accessibility Actions in React Native?
Introduction
Mobile accessibility ensures your app works for users with permanent, temporary, or situational impairments. In React Native, implementing accessibility creates rich experiences that benefit all users while expanding your app's reach. This guide explores accessibility APIs that map to native platform features on both iOS and Android, enabling seamless integration with screen readers like VoiceOver and TalkBack.
Custom actions allow you to define specific behaviors that users can trigger through assistive technologies. For adjustable components like sliders, list items with hidden swipe actions, and complex elements with multiple interactive areas, accessibility actions provide a bridge between visual UI patterns and assistive technology, ensuring users who rely on screen readers have access to your app's full functionality.
What are Accessibility Actions?
React Native provides the accessibilityActions prop to define custom, named actions that components expose to assistive technologies. Unlike touch events that require direct screen interaction, accessibility actions give semantic meaning to interactions like "increment," "decrement," or "activate," helping users understand and trigger specific functionality.
The key difference from touch events is discoverability and invocation. Touch events require users to physically interact with specific screen areas, while accessibility actions are announced by screen readers and triggered through platform-specific gestures. On iOS, users access these through the VoiceOver rotor or by swiping up/down with one finger. Voice Control users can speak action names directly. Android TalkBack users access them through the reading controls menu or local gestures.
React Native seamlessly maps your defined actions to each platform's accessibility services, ensuring your custom actions feel natural to users familiar with platform conventions.
Common Use Cases
Adjustable components like sliders and steppers benefit from increment and decrement actions, giving users precision without drag gestures. Instead of struggling with precise touch control, users simply trigger "increment" or "decrement" actions.
List items with hidden actions are another strong use case. Sighted users may swipe to reveal delete or archive buttons, while accessibility actions expose these hidden functions directly to assistive technology, ensuring features tucked behind gestures remain discoverable.
Components with nested pressable actions can create accessibility challenges when multiple interactive elements compete for focus. By defining specific actions for each function, you flatten complex interactions into a simple list, eliminating nested navigation.
Custom controls maintain platform consistency while extending functionality. Whether you're recreating iOS-style swipe actions or Android's long-press menus, accessibility actions ensure your custom implementations remain as accessible as native counterparts.
Implementation Examples
Custom Slider
Let's build an accessible slider component. First, define the actions with name and label properties:
let incrementAction = { name: "increment", label: "increment" };
let decrementAction = { name: "decrement", label: "decrement" };
These actions are activated by swiping up or down when the component has an accessibilityRole of "adjustable".
Next, create a hook to handle the accessibility actions:
function useOnAccessibilityAction({ updateValue, value }) {
return useEvent(function (e: AccessibilityActionEvent) {
let actionName = e.nativeEvent.actionName;
switch (actionName) {
case incrementAction.name:
updateValue(value + 10);
break;
case decrementAction.name:
updateValue(value - 10);
break;
}
});
}
Finally, compose the component. The View has the accessible prop, ensuring accessibility features are recognized by the operating system:
export function ControlSlider({ max = 100, min = 0, setValue, value }) {
let onAccessibilityAction = useOnAccessibilityAction({
updateValue: setValue,
value,
});
return (
<View
accessible
accessibilityActions={[incrementAction, decrementAction]}
accessibilityHint="Swipe up or down to adjust volume"
accessibilityLabel="Volume"
accessibilityRole="adjustable"
accessibilityValue={{ max, min, now: value }}
onAccessibilityAction={onAccessibilityAction}
>
<Icon name="volume-off" />
<Slider />
<Icon name="volume-high" />
</View>
);
}
Sortable List Item with Multiple Actions
For a sortable list item, define multiple actions that users can trigger:
let toggleAction = { name: "activate", label: "Toggle checked state" };
let upAction = { name: "up", label: "Move item up" };
let downAction = { name: "down", label: "Move item down" };
let topAction = { name: "top", label: "Move item to top" };
let bottomAction = { name: "bottom", label: "Move item to bottom" };
The hook follows the same pattern:
function useOnAccessibilityAction({ id, setMenuItem }) {
return useEvent(function (e: AccessibilityActionEvent) {
let actionName = e.nativeEvent.actionName;
switch (actionName) {
case toggleAction.name:
setMenuItem(id, "toggle");
break;
case upAction.name:
setMenuItem(id, "up");
break;
case downAction.name:
setMenuItem(id, "down");
break;
case topAction.name:
setMenuItem(id, "top");
break;
case bottomAction.name:
setMenuItem(id, "bottom");
break;
}
});
}
Wire everything together in the component:
export function MyMenuEditItem({ item: { icon, id, checked, title }, setMenuItem, onLongPress }) {
let onAccessibilityAction = useOnAccessibilityAction({ id, setMenuItem });
return (
<Pressable
accessible
accessibilityActions={[toggleAction, upAction, downAction, topAction, bottomAction]}
accessibilityHint="Double tap to toggle, swipe up or down for more actions"
accessibilityLabel={title}
accessibilityState={{ checked }}
onAccessibilityAction={onAccessibilityAction}
onLongPress={onLongPress}
onPress={()=> setMenuItem(id, "toggle")}
>
<View>
<Icon name={checked ? "radio-button-on" : "radio-button-off"} />
<Icon name={icon} />
</View>
<Text accessible={false}>{title}</Text>
<Icon name="menu" />
</Pressable>
);
}
Key Takeaways
Accessibility actions transform how users interact with your React Native app through assistive technologies. By implementing the accessibilityActions prop, you:
- Expose hidden functionality to screen reader users
- Simplify complex interactions by flattening nested UI patterns
- Provide precise control for adjustable components without drag gestures
- Maintain platform consistency while building custom controls
The pattern is straightforward: define actions with clear names and labels, handle them in a callback, and wire everything together with the appropriate accessibility props. Both examples demonstrate this pattern for simple sliders and complex sortable lists.
In the next post, we'll explore best practices, platform-specific considerations, and testing strategies to ensure your accessibility implementations work seamlessly across iOS and Android.