โ๏ธ React Native Accessibility
Implement accessible React Native applications using built-in accessibility props and platform-specific APIs.
๐ฏ Why React Native Accessibility Matters
React Native provides accessibility props that map to platform-specific accessibility features, allowing you to create accessible apps that work seamlessly with TalkBack (Android) and VoiceOver (iOS).
๐ป Implementation
Basic Accessibility Props
import React from 'react'; import { View, Text, TouchableOpacity } from 'react-native'; const AccessibleButton = ({ title, onPress, accessibilityLabel }) => { return ( <TouchableOpacity onPress={onPress} accessibilityRole="button" accessibilityLabel={accessibilityLabel || title} accessibilityHint="Double tap to activate" style={{ minHeight: 44, minWidth: 44, padding: 12, backgroundColor: '#007AFF', borderRadius: 8, }} > <Text style={{ color: 'white', textAlign: 'center' }}> {title} </Text> </TouchableOpacity> ); }; export default AccessibleButton;
Accessible Form Input
import React, { useState } from 'react'; import { View, Text, TextInput, TouchableOpacity } from 'react-native'; const AccessibleForm = () => { const [email, setEmail] = useState(''); const [error, setError] = useState(''); const validateEmail = () => { if (!email.includes('@')) { setError('Please enter a valid email address'); } else { setError(''); } }; return ( <View style={{ padding: 20 }}> <Text accessibilityRole="header" style={{ fontSize: 18, fontWeight: 'bold', marginBottom: 10 }} > Contact Form </Text> <Text accessibilityRole="text" style={{ marginBottom: 5 }} > Email Address </Text> <TextInput value={email} onChangeText={setEmail} onBlur={validateEmail} placeholder="Enter your email" accessibilityLabel="Email address input" accessibilityHint="Enter your email address" accessibilityRequired={true} style={{ borderWidth: 1, borderColor: error ? 'red' : '#ccc', padding: 12, borderRadius: 8, marginBottom: 10, }} /> {error ? ( <Text accessibilityRole="alert" accessibilityLiveRegion="polite" style={{ color: 'red', marginBottom: 10 }} > {error} </Text> ) : null} <TouchableOpacity onPress={validateEmail} accessibilityRole="button" accessibilityLabel="Submit form" style={{ backgroundColor: '#007AFF', padding: 12, borderRadius: 8, alignItems: 'center', }} > <Text style={{ color: 'white', fontWeight: 'bold' }}> Submit </Text> </TouchableOpacity> </View> ); }; export default AccessibleForm;
Dynamic Type Support
import React from 'react'; import { View, Text, useWindowDimensions } from 'react-native'; const DynamicTypeText = ({ children, style, ...props }) => { const { fontScale } = useWindowDimensions(); // Ensure minimum readable size const baseFontSize = style?.fontSize || 16; const scaledFontSize = Math.max(baseFontSize * fontScale, 12); return ( <Text style={[ style, { fontSize: scaledFontSize } ]} allowFontScaling={true} {...props} > {children} </Text> ); }; const ResponsiveLayout = () => { return ( <View style={{ padding: 20 }}> <DynamicTypeText accessibilityRole="header" style={{ fontSize: 24, fontWeight: 'bold', marginBottom: 16 }} > Welcome to Our App </DynamicTypeText> <DynamicTypeText accessibilityRole="text" style={{ fontSize: 16, lineHeight: 24 }} > This text will scale with the user's font size preferences while maintaining readability. </DynamicTypeText> </View> ); }; export default ResponsiveLayout;
Accessible Image with Description
import React from 'react'; import { View, Image, Text } from 'react-native'; const AccessibleImage = ({ source, alt, isDecorative = false, style }) => { if (isDecorative) { return ( <Image source={source} style={style} accessibilityRole="image" accessibilityLabel="" accessibilityElementsHidden={true} /> ); } return ( <View> <Image source={source} style={style} accessibilityRole="image" accessibilityLabel={alt} accessibilityHint="Image" /> {alt && ( <Text accessibilityRole="text" style={{ fontSize: 12, color: '#666', marginTop: 4 }} > {alt} </Text> )} </View> ); }; export default AccessibleImage;
โ Best Practices
Do's
- โUse semantic accessibility roles
- โProvide meaningful accessibility labels
- โSet minimum touch target sizes (44x44 points)
- โSupport Dynamic Type scaling
- โTest with both TalkBack and VoiceOver
Don'ts
- โRely only on visual cues
- โUse generic accessibility labels
- โIgnore platform-specific behaviors
- โOverride user font size preferences
- โCreate touch targets smaller than 44x44 points
๐งช Testing Steps
Android Testing
- 1Enable TalkBack: Settings > Accessibility > TalkBack
- 2Test Navigation: Use swipe gestures to navigate
- 3Verify Announcements: Check that labels are descriptive
- 4Test Touch Targets: Ensure easy to tap
iOS Testing
- 1Enable VoiceOver: Settings > Accessibility > VoiceOver
- 2Test Navigation: Use swipe gestures to navigate
- 3Verify Announcements: Check that labels are descriptive
- 4Test Touch Targets: Ensure easy to tap
๐ Common Pitfalls
Missing Labels
Images and buttons without accessibility labels
Generic Roles
Using "button" for all interactive elements
Small Touch Targets
Elements smaller than 44x44 points
No Error Handling
Forms without proper error announcements
๐ฑ Platform-Specific Behavior
Android
Maps to TalkBack and accessibility services
iOS
Maps to VoiceOver and accessibility features
Cross-platform
Maintains consistency across platforms
๐ References
React Native Accessibility Docs โ
Official React Native accessibility documentation
React Native Accessibility Props โ
Complete list of accessibility properties
Platform-Specific Accessibility โ
Platform-specific accessibility implementations