React Aria
React Aria is a collection of unstyled, accessible UI primitives and hooks for building robust and inclusive user interfaces in React. Unlike traditional component libraries that provide pre-styled components, React Aria offers a foundational layer of accessibility, interaction logic, and internationalization, allowing developers complete control over visual styling and DOM structure. The current stable version is 3.48.0, with new features and improvements released frequently, typically on a monthly or bi-monthly cadence. Its core differentiator lies in its 'headless' nature, focusing purely on standardized, accessible behavior by leveraging WAI-ARIA practices, making it an ideal choice for design systems that require highly customizable yet fully accessible components without opinionated styling.
Common errors
-
TypeError: Cannot read properties of null (reading 'focus')
cause This error often occurs when a `ref` required by a React Aria hook is not properly assigned to the corresponding DOM element, or the element is not mounted when the hook tries to interact with it.fixEnsure the `ref` returned by `React.useRef()` is passed to the DOM element (`ref={myRef}`) that the React Aria hook expects to control or observe. Check for conditional rendering that might cause the element to be null when the hook executes. -
Accessibility warning: 'Some element' must have a label accessible to screen readers, or you must provide an `aria-label` or `aria-labelledby` prop.
cause React Aria ensures proper WAI-ARIA roles and attributes, but it relies on the developer to provide semantic content and labels where appropriate. This warning indicates a missing accessible name.fixProvide a visible label as children to the component, or explicitly add an `aria-label` or `aria-labelledby` prop to the element. For example, a button created with `useButton` needs text content or an `aria-label`.
Warnings
- gotcha React Aria provides 'headless' hooks for accessibility and interaction logic, not pre-styled visual components. Developers are responsible for providing all visual styling and the underlying DOM structure. This can be a significant mental model shift for those accustomed to traditional component libraries.
- breaking While `react-aria` itself focuses on primitives, the introduction and evolution of `@adobe/react-spectrum` and `react-aria-components` represents different architectural approaches to building UI. Users of `@adobe/react-spectrum` (a styled component library) or `react-aria-components` (unstyled components built on `react-aria` hooks) should be aware that their APIs and usage patterns differ significantly from directly using `react-aria` hooks.
- gotcha The peer dependencies for `react` and `react-dom` include a wide range of versions (e.g., `^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1`). While this offers flexibility, ensure your project's React version aligns with the tested ranges to avoid potential compatibility issues, especially with pre-release versions.
Install
-
npm install react-aria -
yarn add react-aria -
pnpm add react-aria
Imports
- useButton
const { useButton } = require('react-aria');import { useButton } from 'react-aria'; - useFocusRing
import useFocusRing from 'react-aria/useFocusRing';
import { useFocusRing } from 'react-aria'; - mergeProps
import { mergeProps } from 'react-aria';
Quickstart
import React from 'react';
import { useButton } from 'react-aria';
import { mergeProps } from 'react-aria';
interface MyButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
onPress?: () => void;
}
function MyButton(props: MyButtonProps) {
let ref = React.useRef<HTMLButtonElement>(null);
let { buttonProps, isPressed } = useButton(props, ref);
return (
<button
{...mergeProps(buttonProps, props)}
ref={ref}
style={{
backgroundColor: isPressed ? 'darkblue' : 'blue',
color: 'white',
padding: '10px 20px',
border: 'none',
borderRadius: '5px',
cursor: 'pointer'
}}
>
{props.children}
</button>
);
}
// Example usage in a component
export function App() {
const handlePress = () => {
console.log('Button pressed!');
alert('Hello from React Aria Button!');
};
return (
<div>
<h1>React Aria Quickstart</h1>
<MyButton onPress={handlePress}>Click Me</MyButton>
</div>
);
}