useFocusTrap
Trap Tab/Shift+Tab focus within a container element. Auto-focuses first focusable element on mount, restores focus on unmount.
useFocusTrap keeps Tab and Shift+Tab focus inside a container while it is mounted — the standard requirement for modal dialogs. It is standalone (no provider), moves focus into the container on activation, recaptures focus if it escapes, and restores focus to the previously focused element when the trap releases. Nested traps stack: only the topmost trap captures, and releasing it re-arms the one beneath.
When to use it
- Modal dialogs, confirmation prompts, and other surfaces that must hold focus until dismissed.
- Any overlay where Tab should never reach the page behind it.
A focus trap governs Tab order; it does not close on Escape or lock scroll. Pair it with useKey for Escape-to-close and useScrollLock for background scroll. For focus restoration around triggerless UI without trapping, see useFocusRestore.
Installation
dgadd is not public on npm yet. Until the first release, pack @diffgazer/add from the repository and install that tarball into this app, which is what puts dgadd on pnpm exec.
The hosted registry is not public yet because r.b4r7.dev does not resolve. Use this source checkout or a local registry preview until the endpoint returns 200.
Diffgazer packages are not yet published to npm. Until the first release, pack @diffgazer/ui and @diffgazer/keys from the repository and install those tarballs.
@diffgazer/keys requires no CSS or Tailwind setup.
Parameters
The container ref is the first positional argument; options follow as the second argument.
Returns
Keyboard behavior
Focusable and tabbable are treated as distinct: programmatic recapture may target a tabIndex={-1} container, while Tab cycling only visits tabbable elements. Focus detection and Tab order follow the composed tree. A trap can be rendered inside an open shadow root or contain nested open shadow roots; their tabbable descendants participate in the same order as light-DOM controls. A MutationObserver re-captures focus if the focused element is removed or becomes unfocusable.
Examples
Modal focus trap
Custom initial focus
Notes
Standalone
useFocusTrap does not require KeyboardProvider. It intercepts Tab/Shift+Tab with a capture-phase keydown listener on the container's document while the trap is active, and uses a capture-phase focusin listener there to recapture escaped focus.
Tab wrapping
When focus reaches the last focusable element, Tab wraps to the first. Shift+Tab from the first wraps to the last.
Edge cases
initialFocus. Defaults to the first focusable element. Provide a ref to focus a specific control — for a destructive prompt, point it at Cancel rather than Confirm.restoreFocus. On by default; focus returns to the element that was focused before the trap activated. When an outer trap is still active, focus returns to that trap instead of the pre-trap element.- Container
tabindex. If the container has notabindex, the hook addstabindex="-1"while active and removes it on release so an empty trap can still hold focus. - Open shadow roots. Containers rendered inside an open shadow root and containers with nested open shadow roots use the same initial focus, composed Tab order, escape recapture, and restoration behavior as light-DOM containers. Closed shadow roots remain opaque.
Related
useFocusRestore— restore focus without trapping Tab.useScrollLock— lock background scroll behind a modal.- Focus and scroll guide — composing trap, restore, and scroll lock.
Source
Highlighted source loads after this disclosure opens. Browse the source repository.