DOM / Element Interaction
Hooks that observe a ref'd DOM element (visibility, size, mutations, hover, drag state) or reach into browser chrome (favicon, fullscreen, script loading, the clipboard-adjacent EyeDropper). Every hook here degrades to an inert, SSR-safe default when its underlying API is unsupported.
useClickOutside
Calls onOutside on a mousedown/touchstart whose target falls outside the ref'd element — the standard "close on outside click" pattern for dropdowns, popovers, and modals. Listens on the capture phase, so it still fires even if an inner handler calls stopPropagation().
const ref = useClickOutside<HTMLDivElement>(() => setOpen(false));
return open ? <div ref={ref}>Menu</div> : null;useHover
Boolean hover state for a single ref'd element, via mouseenter/mouseleave.
const { ref, hovered } = useHover<HTMLDivElement>();
return <div ref={ref}>{hovered ? "Hovering" : "Not hovering"}</div>;useMousePosition
Tracks the pointer's clientX/clientY (plus pageX/pageY and screenX/screenY) via window's mousemove event. There's no synchronous read for pointer position, only the event, so this starts at all-0 until the pointer first moves.
const { clientX, clientY } = useMousePosition();useIntersectionObserver
Tracks whether the ref'd element is visible in the viewport (or a given root), via IntersectionObserver. options is passed straight through to the observer (root/rootMargin/threshold). Also exported as useInView, an alias for the same hook.
const { ref, inView } = useIntersectionObserver<HTMLDivElement>();
return <div ref={ref}>{inView ? "Visible" : "Off-screen"}</div>;useResizeObserver
Tracks the ref'd element's content-box size via ResizeObserver.
const { ref, size } = useResizeObserver<HTMLDivElement>();
return (
<div ref={ref}>
{size?.width}×{size?.height}
</div>
);useEventListener
Typed addEventListener wrapper with automatic cleanup — attaches handler for type on target (a RefObject, a DOM node, or window/document), and removes it on unmount or when target/type/options change.
const ref = useRef<HTMLDivElement>(null);
useEventListener(ref, "scroll", (event) => console.log(event));
useEventListener(window, "resize", () => console.log("resized"));useLockBodyScroll
Locks document.body's scroll while locked is true — the standard "no background scroll behind an open modal/drawer" pattern. Restores the body's previous inline overflow on unlock or unmount.
useLockBodyScroll(isModalOpen);useFavicon
Imperatively swaps the <link rel="icon"> href — creates the tag if the document doesn't already have one, and restores the previous href on unmount.
useFavicon(hasUnread ? "/favicon-unread.svg" : "/favicon.svg");useScript
Loads an external <script src> on demand. Concurrent useScript calls for the same src share a single <script> tag — the request is never duplicated. status starts "loading" and becomes "ready"/"error" once the script settles. With removeOnUnmount: true, the tag is removed once the last consumer of that src unmounts.
const { status } = useScript("https://maps.example.com/sdk.js");
if (status === "ready") renderMap();useScrollPosition
window.scrollX/scrollY, updating on the scroll event — window-level; for a specific scrollable element, attach a listener to its ref instead (see useEventListener).
const { y } = useScrollPosition();
const showBackToTop = y > 400;useTextSelection
The current page text selection, via window.getSelection(), updating on the document's selectionchange event.
const text = useTextSelection();
const wordCount = text.trim().split(/\s+/).filter(Boolean).length;useFilePicker
Wraps the File System Access API — showOpenFilePicker/showSaveFilePicker/showDirectoryPicker. Each method resolves undefined (rather than throwing) when the user dismisses the native picker.
Chromium-only, no Safari/Firefox support yet — see MDN's browser compatibility table. supported: false elsewhere, and every method resolves undefined without opening a dialog.
const { showOpenFilePicker, supported } = useFilePicker();
const handles = supported ? await showOpenFilePicker({ multiple: true }) : undefined;useFileDrop
Drag-and-drop file upload state for a single ref'd drop target, via the HTML Drag and Drop API. isOver tracks whether a drag is currently over the element; onDrop is called with the dropped File[]. Also exported as useDropzone, an alias for the same hook.
const { ref, isOver } = useFileDrop<HTMLDivElement>((files) => upload(files));
return <div ref={ref}>{isOver ? "Drop to upload" : "Drag files here"}</div>;useMutationObserver
Observes DOM mutations on the ref'd element/subtree via MutationObserver. Defaults to watching attributes, character data, and the full child subtree; options overrides that.
const ref = useMutationObserver<HTMLDivElement>((mutations) => console.log(mutations));
return <div ref={ref}>{children}</div>;useFullscreen
Fullscreen API wrapper for a single ref'd element — attach ref, then call enter()/exit()/toggle() imperatively (the API requires a user gesture). isFullscreen tracks whether that exact element currently holds fullscreen, via fullscreenchange.
const { ref, isFullscreen, toggle } = useFullscreen<HTMLDivElement>();
return (
<div ref={ref}>
<button onClick={toggle}>{isFullscreen ? "Exit" : "Enter"}</button>
</div>
);usePointer
Unified mouse/touch/pen position and pressure via Pointer events — a modern superset of a plain mousemove-based position hook. isDown reflects whether the primary pointer button/contact is currently active.
const { clientX, clientY, pointerType, isDown } = usePointer();usePopover
Wraps the native Popover API (2024+ baseline) for a single ref'd element carrying the popover attribute — call show()/hide()/toggle() imperatively. isOpen tracks the element's open state via its own toggle event, so it also stays in sync when the browser closes the popover itself (light-dismiss, Esc).
const { ref, isOpen, toggle } = usePopover<HTMLDivElement>();
return (
<>
<button onClick={toggle}>Menu</button>
<div ref={ref} popover="auto">
{isOpen ? "Open" : "Closed"}
</div>
</>
);useViewTransition
Wraps document.startViewTransition() — runs callback (typically a DOM update) inside a native view transition, animating between the before/after states.
Not supported in every browser — see MDN's browser compatibility table. Where unsupported, startTransition just calls callback directly and resolves once it settles — the recommended fallback, per the spec, since the DOM update itself still needs to happen.
const { startTransition } = useViewTransition();
const handleThemeChange = () => startTransition(() => setTheme("dark"));useEyeDropper
Wraps the EyeDropper API — a single-shot native color picker. open() resolves the picked color as an sRGBHex string (e.g. "#ff0000"), or undefined if the user cancels or the API is unsupported.
Chromium-only, no Safari/Firefox support yet — see MDN's browser compatibility table.
const { open, supported } = useEyeDropper();
const hex = supported ? await open() : undefined;