Chris Stevens

Front End Web Developer & Web Accessibility Specialist

Accessibility in React: Building Accessible Components and Applications

12/07/2024

By Chris Stevens

React has revolutionized how we build web applications, but with great power comes great responsibility. As React developers, we must ensure our applications are accessible to all users, including those who rely on assistive technologies. The good news is that React's component-based architecture actually makes it easier to implement and maintain accessibility features when done correctly.

The foundation of accessible React development starts with semantic HTML. Just because we're working with JSX doesn't mean we should forget HTML best practices. In fact, React's virtual DOM makes it even more important to use the right semantic elements. Instead of div-soup, use semantic elements like <nav>, <main>, <article>, <section>, and <aside>. These elements provide crucial context to assistive technologies and improve the overall structure of your application. Remember, a <button> element with proper keyboard handling is always better than a <div> with an onClick event.

ARIA attributes play a crucial role in React applications, especially for dynamic content and custom components. React's JSX syntax makes it straightforward to add ARIA attributes, but there are some gotchas to watch out for. For example, instead of aria-label, you need to use aria-label={string} in JSX. When state changes affect content visibility or functionality, make sure to update relevant ARIA attributes accordingly. Use aria-live regions to announce dynamic content changes, aria-expanded for collapsible sections, and aria-selected for tabbed interfaces. The @react-aria library from Adobe provides excellent React hooks for complex ARIA implementations.

  • Use eslint-plugin-jsx-a11y to catch common accessibility issues during development

  • Implement keyboard navigation with focus management using useRef and focus()

  • Utilize React's useEffect to manage focus when content changes dynamically

  • Consider using established component libraries like @reach/ui or @chakra-ui that prioritize accessibility

  • Test components with screen readers and keyboard navigation regularly

  • Implement skip links for keyboard users to bypass repetitive content

  • Use React.Fragment to avoid unnecessary DOM elements while maintaining semantic structure

Focus management is particularly challenging in React applications, especially with single-page applications (SPAs). When content updates dynamically, we need to ensure that focus is managed appropriately. This might mean returning focus to a trigger button after closing a modal, moving focus to new content after a route change, or maintaining a logical tab order in a complex form. React's useRef hook combined with the focus() method is your friend here. For route changes, consider using a focus management library like react-focus-lock or implementing a focus management system using React Router's hooks.

Forms in React require special attention to accessibility. While controlled components are the React way, we need to ensure they work well with assistive technologies. Use the htmlFor attribute (React's version of 'for') to associate labels with form controls. Implement proper error handling that announces errors to screen readers using aria-invalid and aria-describedby. Consider using form libraries like Formik or React Hook Form, but verify they produce accessible markup and handle keyboard interactions appropriately. Remember to maintain state for both the form data and any error messages that need to be communicated to assistive technologies.

Testing accessibility in React applications should be an integral part of your development process. Tools like Jest and React Testing Library encourage testing that mirrors how users actually interact with your application. React Testing Library's queries like getByRole and getByLabelText help ensure your components are accessible by default. Consider implementing automated accessibility testing using tools like jest-axe for unit tests and Cypress with cypress-axe for end-to-end testing. Regular manual testing with screen readers like VoiceOver or NVDA is also essential, as automated tools can't catch everything.