Skip to main content
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-guard attribute for identification
  • tabIndex={0} to make them focusable
  • Positioned fixed with zero opacity (invisible but functional)
  • No pointer events
  • Inserted at document.body edges (afterbegin and beforeend)
The guards ensure:
  • 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

The hook:
  1. Checks for existing guards on mount
  2. Creates new guards if none exist
  3. Inserts them at document body edges
  4. Increments the reference counter
  5. 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 FocusScope or 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.