Always use React Aria hooks for user interactions instead of native DOM events. React Aria provides robust, accessible, and cross-platform interaction handling.
import { useHover } from '@react-aria/interactions';
function MyComponent() {
const { hoverProps, isHovered } = useHover({});
return (
<div {...hoverProps}>
{isHovered ? 'Hovering!' : 'Not hovering'}
</div>
);
}// ❌ Avoid this - unreliable, not accessible
function MyComponent() {
const [isHovered, setIsHovered] = useState(false);
return (
<div
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
>
{isHovered ? 'Hovering!' : 'Not hovering'}
</div>
);
}- Reliability: Handles edge cases like fast mouse movements and window blur
- Accessibility: Properly supports keyboard navigation and screen readers
- Cross-platform: Works correctly on touch devices (doesn't rely on CSS :hover)
- Browser consistency: Normalizes behavior across different browsers
- useHover: For hover interactions (replaces onMouseEnter/onMouseLeave)
- usePress: For press/click interactions (replaces onClick)
- useFocus: For focus interactions (replaces onFocus/onBlur)
- useKeyboard: For keyboard interactions (replaces onKeyDown/onKeyUp)
Our ESLint configuration enforces these standards:
onMouseEnter,onMouseLeave,onMouseOver,onMouseOutare prohibited- Use
npm run lintto check for violations - Lint errors will prevent builds in CI/CD
In rare cases where React Aria doesn't provide the needed functionality, you may use raw events with:
- Explicit approval in code review
- A comment explaining why React Aria can't be used
- Comprehensive testing for edge cases
// eslint-disable-next-line no-restricted-syntax
<div onMouseEnter={handler}>
{/* Justification: Special case where useHover doesn't apply because... */}
</div>