FocusGuards injects a pair of invisible, focusable elements at the start and end of the document body. These “guards” ensure that focusin and focusout events can be captured consistently, which is essential for focus management in complex UIs.
Installation
Components
FocusGuards
A component that renders its children and ensures focus guards are present in the document.Hook
useFocusGuards
A hook that ensures focus guards are injected without needing to render a component.Usage
Basic Component Usage
Using the Hook
App-level Setup
With Multiple Modals
Conditional Focus Management
How It Works
Focus guards are invisible<span> elements with these properties:
data-radix-focus-guardattribute for identificationtabIndex={0}to make them focusable- Positioned fixed with zero opacity (invisible but functional)
- No pointer events
- Inserted at
document.bodyedges (afterbegin and beforeend)
- Focus events bubble correctly through the document
- Focus trapping can detect when focus leaves the trapped area
- Tab navigation at document boundaries works consistently
Reference Counting
The implementation uses reference counting:- Each call to
useFocusGuards()increments an internal counter - Guards are only removed when the counter reaches zero
- Multiple components can safely use focus guards simultaneously
- Guards are shared across all instances (only one pair exists)
Implementation Details
- Checks for existing guards on mount
- Creates new guards if none exist
- Inserts them at document body edges
- Increments the reference counter
- Decrements counter and removes guards on unmount (if counter reaches 0)
When to Use
Use focus guards when:- Implementing focus trapping (modals, dialogs)
- Building focus management utilities
- Working with
FocusScopeor similar components - Need reliable focus event capture across the document
Notes
Focus guards are automatically shared across all components that use them. You don’t need to worry about duplicate guards - the implementation ensures only one pair exists in the document.
The guards are completely invisible and non-interactive to users. They exist purely to ensure browser focus events work correctly for JavaScript-based focus management.
If you’re using
FocusScope or other Radix UI components that handle focus, you typically need focus guards. Many Radix primitives handle this automatically.The reference counting ensures guards remain in the document as long as any component needs them, and are only removed when no components require them anymore.