@react-aria/focus, react-aria, react-focus-lock, and react-focus-on are all libraries that help manage focus behavior in React applications, but they serve different scopes and use cases. react-aria is a comprehensive suite of React hooks implementing the ARIA specification for accessible UI components, with @react-aria/focus being one of its modular sub-packages focused specifically on focus-related utilities. In contrast, react-focus-lock and react-focus-on are standalone libraries designed primarily to trap or control focus within specific parts of the DOM — commonly used for modals, drawers, or other temporary UI layers that require accessible focus containment.
When building accessible web interfaces in React, controlling focus isn’t just about usability — it’s a core requirement for screen reader users and keyboard-only navigation. The packages @react-aria/focus, react-aria, react-focus-lock, and react-focus-on each address focus management, but with very different philosophies, scopes, and maintenance statuses. Let’s break down what they do, how they differ, and which one fits your project.
react-aria is a complete accessibility framework from Adobe that provides React hooks for building fully accessible UI components according to WAI-ARIA standards. It includes modules for focus, keyboard interaction, screen reader announcements, and more.
// react-aria: Full accessibility hook
import { useButton } from 'react-aria';
function MyButton({ children, ...props }) {
const ref = React.useRef();
const { buttonProps } = useButton(props, ref);
return <button {...buttonProps} ref={ref}>{children}</button>;
}
@react-aria/focus is a sub-package of react-aria that exports only focus-related utilities. You can install it independently if you don’t need the full suite.
// @react-aria/focus: Standalone focus hook
import { useFocusRing } from '@react-aria/focus';
function CustomInput() {
const { focusProps, isFocusVisible } = useFocusRing();
return (
<input
{...focusProps}
className={isFocusVisible ? 'focus-visible' : ''}
/>
);
}
react-focus-lock is a focused, single-purpose library that traps keyboard focus inside a specified DOM subtree — typically used for modals or popovers.
// react-focus-lock: Focus trapping
import { FocusLock } from 'react-focus-lock';
function Modal({ isOpen, children }) {
if (!isOpen) return null;
return (
<FocusLock>
<div role="dialog" aria-modal="true">
{children}
</div>
</FocusLock>
);
}
react-focus-on was an earlier alternative to react-focus-lock that combined focus trapping with scroll locking and inert behavior. However, it is now deprecated.
// ❌ DO NOT USE — react-focus-on is deprecated
// import { FocusOn } from 'react-focus-on'; // Avoid in new code
⚠️ Deprecation Notice: As confirmed in its GitHub repository and npm page,
react-focus-onis no longer maintained. The author recommends migrating toreact-focus-lock.
Focus trapping ensures that tabbing through a modal doesn’t let users accidentally navigate into the background page — a critical accessibility requirement.
react-focus-lock excels here. It automatically finds focusable elements, handles dynamic content, supports portals, and works with shadow DOM.
// react-focus-lock handles trapping robustly
<FocusLock returnFocus>
<div>
<input placeholder="First field" />
<button>Close</button>
{/* Focus cycles between these two */}
</div>
</FocusLock>
@react-aria/focus does not provide focus trapping. It offers utilities like useFocusWithin to detect if focus is inside a container, but not to enforce containment.
// @react-aria/focus: Detects focus, doesn't trap it
import { useFocusWithin } from '@react-aria/focus';
function Panel() {
const { focusWithinProps } = useFocusWithin({
onFocusWithinChange: (isFocused) => console.log(isFocused)
});
return <div {...focusWithinProps}>...</div>;
}
react-aria also lacks built-in focus trapping. Instead, it expects you to compose trapping behavior using external tools like react-focus-lock alongside its component hooks.
react-focus-on, while once capable of trapping, is obsolete and should not be considered.
Sometimes you need to show a visible focus ring only when the user is navigating via keyboard — not when clicking with a mouse. This improves aesthetics without sacrificing accessibility.
@react-aria/focus provides useFocusRing for exactly this purpose, following the WHATWG focus visible heuristic.
// @react-aria/focus: Smart focus ring
import { useFocusRing } from '@react-aria/focus';
function Button() {
const { isFocusVisible, focusProps } = useFocusRing();
return (
<button
{...focusProps}
style={{ outline: isFocusVisible ? '2px solid blue' : 'none' }}
>
Click me
</button>
);
}
react-focus-lock does not handle focus styling. It’s purely about focus movement, not visual feedback.
react-aria includes useFocusRing as part of its full API, so you get the same behavior when using the umbrella package.
react-focus-on never provided focus ring utilities.
If you’re building a design system or complex component library, react-aria offers the most cohesive experience. Its hooks are designed to work together — for example, useDialog (from @react-aria/dialog) pairs naturally with focus management, but still requires an external focus trap like react-focus-lock for full modal behavior.
// Combining react-aria + react-focus-lock
import { useDialog } from 'react-aria';
import { FocusLock } from 'react-focus-lock';
function AccessibleModal({ title, children }) {
const ref = React.useRef();
const { dialogProps, titleProps } = useDialog({ 'aria-label': title }, ref);
return (
<FocusLock>
<div {...dialogProps} ref={ref} role="dialog" aria-modal="true">
<h2 {...titleProps}>{title}</h2>
{children}
</div>
</FocusLock>
);
}
In contrast, react-focus-lock is framework-agnostic and composes easily with any React codebase, even without using ARIA hooks. It’s often the go-to for teams using plain React or other UI libraries (like Material UI or Chakra) that don’t include their own focus trapping.
react-focus-on — it’s deprecated. Migrate existing usage to react-focus-lock.@react-aria/focus alone if you need focus trapping — it doesn’t provide it.react-aria if you only need focus trapping — it’s overkill and adds unnecessary bundle weight.react-focus-lock if you need ARIA-compliant component logic (like menu navigation or combobox behavior) — it only handles focus containment, not interaction patterns.| Feature | @react-aria/focus | react-aria | react-focus-lock | react-focus-on |
|---|---|---|---|---|
| Focus trapping | ❌ | ❌ | ✅ | ✅ (but deprecated) |
| Focus ring visibility | ✅ | ✅ | ❌ | ❌ |
| Part of larger ARIA lib | ✅ (sub-module) | ✅ (full suite) | ❌ | ❌ |
| Standalone utility | ✅ | ❌ | ✅ | ✅ (deprecated) |
| Recommended for new projects | ✅ (if using React Aria) | ✅ (for full ARIA needs) | ✅ | ❌ |
react-focus-lock for reliable focus trapping.@react-aria/focus for focus ring logic.react-aria for end-to-end ARIA compliance.react-focus-on? → Plan a migration to react-focus-lock.Focus management is non-negotiable for accessibility. Choose the right tool based on whether you need containment, styling, or full ARIA semantics — and avoid deprecated solutions that won’t receive updates or security fixes.
Choose @react-aria/focus if you're already using or planning to adopt the broader React Aria ecosystem and need low-level, standards-compliant focus management utilities like useFocusRing, useFocusWithin, or useAutoFocus. It’s ideal for building custom accessible components that align with WAI-ARIA patterns without reinventing focus logic.
Choose react-focus-lock if your primary need is reliable, performant focus trapping for overlays like modals, dialogs, or side panels. It handles edge cases like portals, scroll locking, and inert content gracefully, and integrates well with any React architecture without requiring adoption of a larger framework.
Choose react-aria when you need a full-featured, cohesive set of accessibility hooks covering not just focus but also keyboard navigation, screen reader announcements, internationalization, and component-specific ARIA patterns (e.g., for menus, tabs, sliders). It’s best suited for design systems or complex UI libraries where consistency and compliance are critical.
Avoid react-focus-on in new projects — it has been officially deprecated by its author in favor of react-focus-lock. While it once offered similar focus-trapping capabilities with additional features like scroll locking, it is no longer maintained and should not be used for production applications.
This package is part of react-spectrum. See the repo for more details.