@philiprehberger/react-hooks
v0.2.7
Published
Reusable React hooks - scroll lock, focus trap, swipe gestures, debounce, keyboard shortcuts, image preloading, and more
Readme
@philiprehberger/react-hooks
Reusable React hooks for common UI patterns
Installation
npm install @philiprehberger/react-hooksHooks
useBodyScrollLock(isLocked: boolean)
Lock body scroll when a condition is true. Preserves and restores scroll position
import { useBodyScrollLock } from '@philiprehberger/react-hooks';
function Modal({ isOpen }: { isOpen: boolean }) {
useBodyScrollLock(isOpen);
return isOpen ? <div className="modal">...</div> : null;
}useFocusTrap<T extends HTMLElement>(isActive: boolean)
Trap keyboard focus within a container. Returns a ref to attach to the container element.
import { useFocusTrap } from '@philiprehberger/react-hooks';
function Dialog({ isOpen }: { isOpen: boolean }) {
const ref = useFocusTrap<HTMLDivElement>(isOpen);
return <div ref={ref}>...</div>;
}useSwipeGesture<T extends HTMLElement>(options: SwipeOptions)
Detect touch swipe gestures. Returns a ref to attach to the target element.
import { useSwipeGesture } from '@philiprehberger/react-hooks';
function Drawer({ onClose }: { onClose: () => void }) {
const ref = useSwipeGesture<HTMLDivElement>({
onSwipeLeft: onClose,
threshold: 80,
});
return <div ref={ref}>...</div>;
}Usage
import {
useBodyScrollLock,
useFocusTrap,
useDebounce,
useKeyboardShortcuts,
} from '@philiprehberger/react-hooks';
function Modal({ isOpen }: { isOpen: boolean }) {
useBodyScrollLock(isOpen);
const ref = useFocusTrap<HTMLDivElement>(isOpen);
return isOpen ? <div ref={ref}>Modal content</div> : null;
}
function SearchInput() {
const [query, setQuery] = useState('');
const debouncedQuery = useDebounce(query, 300);
useEffect(() => {
if (debouncedQuery) fetchResults(debouncedQuery);
}, [debouncedQuery]);
return <input value={query} onChange={(e) => setQuery(e.target.value)} />;
}API
Hooks
| Hook | Signature | Description |
|------|-----------|-------------|
| useBodyScrollLock | (isLocked: boolean) => void | Lock body scroll when a condition is true |
| useFocusTrap | <T extends HTMLElement>(isActive: boolean) => RefObject<T> | Trap keyboard focus within a container |
| useSwipeGesture | <T extends HTMLElement>(options: SwipeOptions) => RefObject<T> | Detect touch swipe gestures |
| useDebounce | <T>(value: T, delay?: number) => T | Debounce a value (default: 500ms) |
| useDebouncedCallback | <T>(callback: T, delay?: number) => T | Debounce a callback function |
| usePrefersReducedMotion | () => boolean | Detect user's reduced motion preference |
| useKeyboardShortcuts | (shortcuts: KeyboardShortcut[], options?) => KeyboardShortcut[] | Register keyboard shortcuts with modifier key support |
| useKeyboardNavigation | (options: UseKeyboardNavigationOptions) => UseKeyboardNavigationReturn | Keyboard navigation for lists and menus (roving tabindex) |
| useImagePreload | (options: UseImagePreloadOptions) => UseImagePreloadReturn | Preload images with progress tracking |
Utilities
| Function | Signature | Description |
|----------|-----------|-------------|
| formatShortcut | (shortcut: KeyboardShortcut) => string | Format a shortcut for display (e.g., "Ctrl+S") |
| getShortcutKeys | (shortcut: KeyboardShortcut) => string[] | Get individual key parts for rendering badges |
| groupShortcutsByCategory | (shortcuts: KeyboardShortcut[]) => Map<string, KeyboardShortcut[]> | Group shortcuts by category |
| preloadImage | (src: string) => Promise<HTMLImageElement> | Preload a single image imperatively |
| preloadImages | (sources: string[], onProgress?) => Promise<HTMLImageElement[]> | Preload multiple images with progress callback |
Development
npm install
npm run build
npm testLicense
MIT
