useFocusTrap
azemmur/hooks-use-focus-trapFreeHook
A hook to trap focus within a container element for accessibility.
Install it
npx shadcn@latest add https://azemmur.raouf.codes/r/hooks-use-focus-trap.jsonSource
1// Copyright (c) 2026 raouf.codes - Azemmur23'use client';45import { useEffect } from 'react';67const FOCUSABLE_SELECTORS = [8 'a[href]',9 'button:not([disabled])',10 'textarea:not([disabled])',11 'input:not([disabled])',12 'select:not([disabled])',13 '[tabindex]:not([tabindex="-1"])',14].join(',');1516/**17 * Hook to trap focus within a container element.18 *19 * When active, Tab and Shift+Tab will cycle through focusable elements20 * within the container, preventing focus from escaping.21 *22 * @example23 * ```tsx24 * const containerRef = useRef<HTMLDivElement>(null);25 * useFocusTrap(isOpen, containerRef);26 *27 * return <div ref={containerRef}>...</div>;28 * ```29 *30 * @param active - Whether the focus trap is active31 * @param containerRef - Ref to the container element32 */33export function useFocusTrap(34 active: boolean,35 containerRef: React.RefObject<HTMLElement | null>,36) {37 useEffect(() => {38 const container = containerRef.current;39 if (!active || !container) return;4041 const handleTabKey = (e: KeyboardEvent) => {42 if (e.key !== 'Tab') return;4344 const focusableElements =45 container.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTORS);4647 if (focusableElements.length === 0) return;4849 const firstElement = focusableElements[0];50 const lastElement = focusableElements[focusableElements.length - 1];5152 if (!firstElement || !lastElement) return;5354 if (e.shiftKey) {55 if (document.activeElement === firstElement) {56 e.preventDefault();57 lastElement.focus();58 }59 } else {60 if (document.activeElement === lastElement) {61 e.preventDefault();62 firstElement.focus();63 }64 }65 };6667 const focusableElements =68 container.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTORS);69 const firstElement = focusableElements[0];70 if (firstElement) {71 firstElement.focus();72 }7374 container.addEventListener('keydown', handleTabKey);7576 return () => {77 container.removeEventListener('keydown', handleTabKey);78 };79 }, [active, containerRef]);80}
Files it writes
More from Azemmur
All 27 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| useIsMobilehooks-use-is-mobile | Azemmur | Hooks | Free | no deps | |
| useKeyActionshooks-use-key-actions | Azemmur | Hooks | Free | no deps | |
| useMergeRefshooks-use-merged-refs | Azemmur | Hooks | Free | no deps | |
| usePinchZoomhooks-use-pinch-zoom | Azemmur | Hooks | Free | no deps |
