Traps keyboard focus inside a container element while enabled. When the user presses Tab or Shift+Tab, focus cycles through only the focusable elements within the container. Essential for modals, drawers, and other overlay patterns to maintain accessibility.
Import
import { useFocusTrap } from '@kivora/react';
Usage
import { useRef, useState } from 'react';
import { useFocusTrap } from '@kivora/react';
function Modal({ isOpen, onClose }) {
const modalRef = useRef<HTMLDivElement>(null);
useFocusTrap(modalRef, isOpen);
if (!isOpen) return null;
return (
<div ref={modalRef} className="modal">
<h2>Modal Title</h2>
<button>First Button</button>
<input type="text" placeholder="Enter text" />
<button onClick={onClose}>Close</button>
</div>
);
}
Parameters
ref
RefObject<T | null>
required
A React ref object pointing to the container element that should trap focus.
Whether focus trapping is active. Typically tied to the open/closed state of a modal or overlay.
Returns
void - This hook does not return a value.
Behavior
- Auto-focus: When enabled, automatically focuses the first focusable element
- Tab cycling: Tab moves forward through focusable elements, wrapping to the first when reaching the last
- Shift+Tab cycling: Shift+Tab moves backward, wrapping to the last when at the first
- Focus restoration: Returns focus to the previously focused element when disabled
- Focusable elements: Targets
a[href], button, input, select, textarea, and elements with tabindex (excluding tabindex="-1")
Examples
Dialog with Focus Trap
function Dialog({ open, onClose, children }) {
const dialogRef = useRef<HTMLDialogElement>(null);
useFocusTrap(dialogRef, open);
return (
<dialog ref={dialogRef} open={open}>
{children}
<button onClick={onClose}>OK</button>
<button onClick={onClose}>Cancel</button>
</dialog>
);
}
Drawer Component
function Drawer({ isOpen, onClose }) {
const drawerRef = useRef<HTMLDivElement>(null);
useFocusTrap(drawerRef, isOpen);
return (
<>
{isOpen && <div className="overlay" onClick={onClose} />}
<div ref={drawerRef} className={isOpen ? 'drawer open' : 'drawer'}>
<nav>
<a href="/home">Home</a>
<a href="/about">About</a>
<a href="/contact">Contact</a>
</nav>
<button onClick={onClose}>Close</button>
</div>
</>
);
}
function PopoverMenu({ trigger }) {
const [isOpen, setIsOpen] = useState(false);
const menuRef = useRef<HTMLDivElement>(null);
useFocusTrap(menuRef, isOpen);
return (
<div>
<button onClick={() => setIsOpen(true)}>{trigger}</button>
{isOpen && (
<div ref={menuRef} className="popover">
<button onClick={() => console.log('Edit')}>Edit</button>
<button onClick={() => console.log('Delete')}>Delete</button>
<button onClick={() => setIsOpen(false)}>Cancel</button>
</div>
)}
</div>
);
}
Accessibility
- Implements ARIA Authoring Practices Guide pattern for focus management in dialogs and overlays
- Prevents keyboard users from tabbing out of modal content into background page
- Automatically restores focus to the trigger element when modal closes
- Works with screen readers by maintaining logical focus flow