Comprehensive hooks for DOM events, user interactions, measurements, viewport tracking, and browser API integration.
Triggers callback when clicking outside target element(s), commonly used for modals and dropdowns.
/**
* Triggers callback when clicking outside target element(s)
* @param onClickAway - Callback function to execute on outside click
* @param target - Element(s) to monitor for outside clicks
* @param eventName - DOM event name(s) to listen for (default: 'click')
*/
function useClickAway<T extends Event = Event>(
onClickAway: (event: T) => void,
target: BasicTarget | BasicTarget[],
eventName?: DocumentEventKey | DocumentEventKey[]
): void;
type BasicTarget<T = Element> = (() => T | null) | T | null | MutableRefObject<T>;
type DocumentEventKey = keyof DocumentEventMap;Usage Example:
import { useClickAway } from 'ahooks';
import { useRef, useState } from 'react';
function Dropdown() {
const [visible, setVisible] = useState(false);
const ref = useRef<HTMLDivElement>(null);
useClickAway(() => {
setVisible(false);
}, ref);
return (
<div ref={ref}>
<button onClick={() => setVisible(!visible)}>
Toggle Dropdown
</button>
{visible && (
<ul className="dropdown">
<li>Option 1</li>
<li>Option 2</li>
<li>Option 3</li>
</ul>
)}
</div>
);
}Manages event listeners with automatic cleanup and support for multiple target types.
/**
* Manages event listeners with automatic cleanup
* Multiple overloads for different target types
*/
function useEventListener<K extends keyof HTMLElementEventMap>(
eventName: K,
handler: (ev: HTMLElementEventMap[K]) => void,
options?: Options<HTMLElement>
): void;
function useEventListener<K extends keyof ElementEventMap>(
eventName: K,
handler: (ev: ElementEventMap[K]) => void,
options?: Options<Element>
): void;
function useEventListener<K extends keyof DocumentEventMap>(
eventName: K,
handler: (ev: DocumentEventMap[K]) => void,
options?: Options<Document>
): void;
function useEventListener<K extends keyof WindowEventMap>(
eventName: K,
handler: (ev: WindowEventMap[K]) => void,
options?: Options<Window>
): void;
interface Options<T> {
/** Target element, document, window, or ref */
target?: BasicTarget<T>;
/** Event listener options */
capture?: boolean;
once?: boolean;
passive?: boolean;
}Usage Example:
import { useEventListener } from 'ahooks';
import { useRef } from 'react';
function KeyboardHandler() {
const [lastKey, setLastKey] = useState('');
const divRef = useRef<HTMLDivElement>(null);
// Listen to document keydown
useEventListener('keydown', (event) => {
setLastKey(event.key);
}, {
target: document
});
// Listen to specific element click
useEventListener('click', (event) => {
console.log('Div clicked:', event);
}, {
target: divRef
});
// Listen to window resize
useEventListener('resize', () => {
console.log('Window resized');
}, {
target: window
});
return (
<div ref={divRef}>
<p>Last key pressed: {lastKey}</p>
<p>Click this div or press any key</p>
</div>
);
}Tracks hover state of an element with enter/leave callbacks.
/**
* Tracks hover state of an element
* @param target - Element to track hover state for
* @param options - Configuration with hover callbacks
* @returns Boolean indicating if element is currently hovered
*/
function useHover(target: BasicTarget, options?: Options): boolean;
interface Options {
/** Callback when mouse enters */
onEnter?: () => void;
/** Callback when mouse leaves */
onLeave?: () => void;
/** Callback when hover state changes */
onChange?: (isHovering: boolean) => void;
}Usage Example:
import { useHover } from 'ahooks';
import { useRef } from 'react';
function HoverCard() {
const ref = useRef<HTMLDivElement>(null);
const isHovering = useHover(ref, {
onEnter: () => console.log('Mouse entered'),
onLeave: () => console.log('Mouse left'),
onChange: (hovering) => console.log('Hover state:', hovering)
});
return (
<div
ref={ref}
style={{
padding: '20px',
backgroundColor: isHovering ? 'lightblue' : 'lightgray',
transition: 'background-color 0.3s'
}}
>
{isHovering ? 'Currently hovering!' : 'Hover me!'}
</div>
);
}Tracks focus within element (like CSS :focus-within selector) with focus/blur callbacks.
/**
* Tracks focus within element (like :focus-within CSS selector)
* @param target - Element to track focus within
* @param options - Configuration with focus callbacks
* @returns Boolean indicating if any descendant element has focus
*/
function useFocusWithin(target: BasicTarget, options?: Options): boolean;
interface Options {
/** Callback when focus enters the element or its children */
onFocus?: (e: FocusEvent) => void;
/** Callback when focus leaves the element and its children */
onBlur?: (e: FocusEvent) => void;
/** Callback when focus-within state changes */
onChange?: (isFocusWithin: boolean) => void;
}Handles keyboard key press events with flexible key filtering and target options.
/**
* Handles keyboard key press events with flexible filtering
* @param keyFilter - Key(s) to listen for or filter function
* @param eventHandler - Handler function when matching keys are pressed
* @param options - Configuration options
*/
function useKeyPress(
keyFilter: KeyFilter,
eventHandler: (event: KeyboardEvent, key: KeyType) => void,
options?: Options
): void;
type KeyFilter = KeyType | KeyType[] | ((event: KeyboardEvent) => boolean);
type KeyType = number | string;
type KeyEvent = 'keydown' | 'keyup';
interface Options {
/** Events to listen for (default: ['keydown']) */
events?: KeyEvent[];
/** Target element (default: document) */
target?: BasicTarget;
/** Require exact match for modifier keys (default: false) */
exactMatch?: boolean;
/** Use capture phase (default: false) */
useCapture?: boolean;
}Usage Example:
import { useKeyPress } from 'ahooks';
function KeyboardShortcuts() {
const [message, setMessage] = useState('');
// Listen for specific key
useKeyPress('Enter', (event) => {
setMessage('Enter pressed!');
});
// Listen for multiple keys
useKeyPress(['ctrl.s', 'cmd.s'], (event) => {
event.preventDefault();
setMessage('Save shortcut pressed!');
});
// Listen for arrow keys
useKeyPress(['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'], (event, key) => {
setMessage(`Arrow key pressed: ${key}`);
});
// Custom key filter function
useKeyPress((event) => event.key >= '0' && event.key <= '9', (event) => {
setMessage(`Number pressed: ${event.key}`);
});
return (
<div>
<p>Press Enter, Ctrl+S, arrow keys, or number keys</p>
<p>Last action: {message}</p>
</div>
);
}Handles long press gestures with customizable timing and movement threshold.
/**
* Handles long press gestures
* @param onLongPress - Callback when long press is detected
* @param target - Element to attach long press to
* @param options - Configuration options
*/
function useLongPress(
onLongPress: (event: EventType) => void,
target: BasicTarget,
options?: Options
): void;
type EventType = MouseEvent | TouchEvent;
interface Options {
/** Long press duration in ms (default: 350) */
delay?: number;
/** Movement threshold to cancel long press */
moveThreshold?: { x?: number; y?: number };
/** Callback for normal click */
onClick?: (event: EventType) => void;
/** Callback when long press ends */
onLongPressEnd?: (event: EventType) => void;
}Tracks element size changes using ResizeObserver.
/**
* Tracks element size changes using ResizeObserver
* @param target - Element to track size for
* @returns Object with width and height, or undefined if element not found
*/
function useSize(target: BasicTarget): Size | undefined;
interface Size {
width: number;
height: number;
}Usage Example:
import { useSize } from 'ahooks';
import { useRef } from 'react';
function ResizableDiv() {
const ref = useRef<HTMLDivElement>(null);
const size = useSize(ref);
return (
<div>
<div
ref={ref}
style={{
width: '50%',
height: '200px',
backgroundColor: 'lightblue',
resize: 'both',
overflow: 'auto'
}}
>
Resize me!
</div>
{size && (
<p>
Size: {Math.round(size.width)} x {Math.round(size.height)}
</p>
)}
</div>
);
}Tracks scroll position of an element or window.
/**
* Tracks scroll position of element or window
* @param target - Element to track scroll for (default: document)
* @param shouldUpdate - Function to determine if position should update
* @returns Object with scroll position or undefined
*/
function useScroll(
target?: BasicTarget,
shouldUpdate?: (position: Position) => boolean
): Position | undefined;
interface Position {
left: number;
top: number;
}Tracks element visibility in viewport using Intersection Observer.
/**
* Tracks element visibility in viewport using Intersection Observer
* @param target - Element(s) to track viewport intersection
* @param options - Intersection Observer options
* @returns Array with visibility boolean and intersection ratio
*/
function useInViewport(
target: BasicTarget | BasicTarget[],
options?: Options
): [boolean?, number?];
interface Options {
/** Root margin for intersection detection */
rootMargin?: string;
/** Threshold(s) for intersection detection */
threshold?: number | number[];
/** Root element for intersection (default: viewport) */
root?: BasicTarget<Element>;
/** Callback when intersection changes */
callback?: (entry: IntersectionObserverEntry) => void;
}Tracks comprehensive mouse position and element relationship data.
/**
* Tracks comprehensive mouse position data
* @param target - Element to track mouse relative to (optional)
* @returns Object with detailed cursor position information
*/
function useMouse(target?: BasicTarget): CursorState;
interface CursorState {
/** Screen X coordinate */
screenX: number;
/** Screen Y coordinate */
screenY: number;
/** Client X coordinate (relative to viewport) */
clientX: number;
/** Client Y coordinate (relative to viewport) */
clientY: number;
/** Page X coordinate (including scroll) */
pageX: number;
/** Page Y coordinate (including scroll) */
pageY: number;
/** X coordinate relative to target element */
elementX: number;
/** Y coordinate relative to target element */
elementY: number;
/** Target element height */
elementH: number;
/** Target element width */
elementW: number;
/** Target element X position */
elementPosX: number;
/** Target element Y position */
elementPosY: number;
}Manages fullscreen state for elements with comprehensive control actions.
/**
* Manages fullscreen state for elements
* @param target - Element to make fullscreen
* @param options - Configuration options
* @returns Array with fullscreen state and control actions
*/
function useFullscreen(
target: BasicTarget,
options?: Options
): [boolean, FullscreenActions];
interface Options {
/** Callback when exiting fullscreen */
onExit?: () => void;
/** Callback when entering fullscreen */
onEnter?: () => void;
/** Enable page-level fullscreen (CSS-based) */
pageFullscreen?: boolean | PageFullscreenOptions;
}
interface PageFullscreenOptions {
className?: string;
zIndex?: number;
}
interface FullscreenActions {
/** Enter fullscreen mode */
enterFullscreen: () => void;
/** Exit fullscreen mode */
exitFullscreen: () => void;
/** Toggle fullscreen mode */
toggleFullscreen: () => void;
/** Whether fullscreen is supported */
isEnabled: boolean;
}Tracks text selection within target element or document.
/**
* Tracks text selection within target element or document
* @param target - Element to track text selection within (optional)
* @returns Object with selection text and position information
*/
function useTextSelection(target?: BasicTarget): TextSelectionState;
interface TextSelectionState {
/** Selected text content */
text: string;
/** Selection left position */
left: number;
/** Selection top position */
top: number;
/** Selection height */
height: number;
/** Selection width */
width: number;
}Observes DOM mutations using MutationObserver API.
/**
* Observes DOM mutations using MutationObserver
* @param target - Element(s) to observe for mutations
* @param callback - Function called when mutations occur
* @param options - MutationObserver configuration options
*/
function useMutationObserver(
target: BasicTarget | BasicTarget[],
callback: MutationCallback,
options?: MutationObserverInit
): void;
// MutationCallback and MutationObserverInit are standard DOM typesUsage Example:
import { useMutationObserver } from 'ahooks';
import { useRef, useState } from 'react';
function MutationTracker() {
const ref = useRef<HTMLDivElement>(null);
const [mutations, setMutations] = useState<string[]>([]);
useMutationObserver(
ref,
(mutationsList) => {
const newMutations = mutationsList.map(mutation => {
return `${mutation.type}: ${mutation.target.nodeName}`;
});
setMutations(prev => [...prev, ...newMutations].slice(-10));
},
{
childList: true,
subtree: true,
attributes: true,
attributeOldValue: true
}
);
const addChild = () => {
if (ref.current) {
const div = document.createElement('div');
div.textContent = `Child ${Date.now()}`;
ref.current.appendChild(div);
}
};
return (
<div>
<button onClick={addChild}>Add Child</button>
<div ref={ref} style={{ border: '1px solid black', padding: '10px' }}>
Mutations will be tracked here
</div>
<ul>
{mutations.map((mutation, index) => (
<li key={index}>{mutation}</li>
))}
</ul>
</div>
);
}// React ref types
type MutableRefObject<T> = import('react').MutableRefObject<T>;
// Target element types
type BasicTarget<T = Element> = (() => T | null) | T | null | MutableRefObject<T>;
// DOM event map types
type DocumentEventKey = keyof DocumentEventMap;
type WindowEventKey = keyof WindowEventMap;