Register
Usage#
import { useFocusTrap } from '@primereact/headless/focustrap';const { containerRef, firstHiddenElementRef, lastHiddenElementRef, firstHiddenProps, lastHiddenProps } = useFocusTrap();
<>
<span ref={firstHiddenElementRef} tabIndex={0} {...firstHiddenProps} className="sr-only" />
<div ref={containerRef}></div>
<span ref={lastHiddenElementRef} tabIndex={0} {...lastHiddenProps} className="sr-only" />
</>;useFocusTrap keeps focus inside a container while it is active, managing initial focus, tab cycling, and escape dismissal. See Primitive for a component-based API.
Features#
- Boundary wiring —
containerRefplusfirstHiddenElementRef/lastHiddenElementRefdefine the trap edges with invisible sentinel spans - Auto focus strategy — on mount, focus prioritizes
initialFocusRef, then[autofocus]/[data-autofocus], then the first focusable element - Tab cycling — focus wraps from the last element back to the first and vice versa, with override hooks for both edges
- Dynamic content — a MutationObserver refocuses when children are added or removed from the container
- Escape handling —
onEscapefires when the user presses Escape inside the trap so you can close the surrounding UI
Working with callbacks#
Direct initial focus#
Pass initialFocusRef to land focus on a specific element, which takes priority over any autoFocus attributes.
const inputRef = React.useRef<HTMLInputElement>(null);
const focustrap = useFocusTrap({ initialFocusRef: inputRef });Close on Escape#
Wire onEscape to dismiss the surrounding dialog or menu when the user presses Escape.
const focustrap = useFocusTrap({
onEscape: () => setOpen(false)
});Override tab wrap-around#
Replace the default wrap behavior with onTabFirst and onTabLast when you need custom focus flow — for example, moving focus to an external element when the user tabs past the last trapped element.
const focustrap = useFocusTrap({
onTabFirst: (e) => focusPreviousPanel(),
onTabLast: (e) => focusNextPanel()
});Temporarily disable the trap#
Toggle trapped or autoFocus when the trap should be mounted but inactive — for example, while an inner modal owns focus.
const focustrap = useFocusTrap({ trapped: isActive, autoFocus: isActive });Styling with data attributes#
The container receives a data-focus-trap attribute when the trap is active, giving you a hook for debugging or visual outlines.
[data-focus-trap] {
outline: 2px solid var(--p-primary-color);
}API#
useFocusTrap#
| Name | Type | Default |
|---|---|---|
trapped | boolean | true |
| When enabled, focus is trapped within the container element. | ||
autoFocus | boolean | true |
| When enabled, the first focusable element receives focus on mount. | ||
initialFocusRef | RefObject<HTMLElement> | — |
| Reference to the element that should receive focus when the trap activates. Takes priority over autoFocus and [autofocus] attribute detection. | ||
onEscape | (event: KeyboardEvent) => void | — |
| Callback invoked when the Escape key is pressed inside the trap. | ||
onTabFirst | (event: FocusEvent<HTMLElement>) => void | — |
| Callback invoked when Shift+Tab is pressed before the first focusable element. When provided, overrides the default cycling behavior. | ||
onTabLast | (event: FocusEvent<HTMLElement>) => void | — |
| Callback invoked when Tab is pressed past the last focusable element. When provided, overrides the default cycling behavior. | ||
Accessibility#
Tab cycles focusable elements inside, Shift+Tab reverses, and focus returns to the trigger when unmounted. See Primitive for full WAI-ARIA compliance details.