Try Live
Add Docs
Rankings
Pricing
Enterprise
Docs
Install
Theme
Install
Docs
Pricing
Enterprise
More...
More...
Try Live
Rankings
Create API Key
Add Docs
Reactuse
https://github.com/childrentime/reactuse
Admin
Reactuse is a collection of essential React Hooks Utilities, providing a set of reusable hooks to
...
Tokens:
238,772
Snippets:
2,193
Trust Score:
9.4
Update:
1 week ago
Context
Skills
Chat
Benchmark
95.4
Suggestions
Latest
Show doc for...
Code
Info
Show Results
Context Summary (auto-generated)
Raw
Copy
Link
# ReactUse (@reactuses/core) ReactUse is a comprehensive collection of 100+ essential React Hooks for building modern React applications, inspired by VueUse. It provides production-ready, tree-shakable hooks organized into four categories — Browser, State, Element, and Effect — covering everything from clipboard access and media queries to drag-and-drop, intersection observation, and async effects. The library is fully typed with TypeScript, SSR-compatible for use with Next.js and Remix, and ships with an optional MCP (Model Context Protocol) server for AI-powered hook discovery. At its core, the library follows a consistent design philosophy: hooks accept `BasicTarget` values (refs, direct elements, or selector functions) for flexible DOM targeting; browser-environment checks prevent SSR crashes; event listeners and observers are cleaned up automatically; and stable function references via `useEvent`/`useLatest` eliminate common infinite-render pitfalls. Version 6.3.1 (the current release) requires React 19 as a peer dependency and supports concurrent mode. --- ## Installation ```bash npm i @reactuses/core # or pnpm add @reactuses/core # or yarn add @reactuses/core ``` --- ## State Hooks ### useToggle — toggle a boolean on/off Manages a boolean value and returns a toggler that optionally accepts a forced value. ```tsx import { useToggle } from "@reactuses/core"; function ToggleDemo() { const [on, toggle] = useToggle(false); return ( <div> <p>Status: {on ? "ON" : "OFF"}</p> <button onClick={() => toggle()}>Toggle</button> <button onClick={() => toggle(true)}>Force ON</button> <button onClick={() => toggle(false)}>Force OFF</button> </div> ); } ``` --- ### useBoolean — boolean state with named helpers Returns an object with `setTrue`, `setFalse`, and `toggle` for ergonomic boolean state management. ```tsx import { useBoolean } from "@reactuses/core"; function Modal() { const { value: isOpen, setTrue: open, setFalse: close, toggle } = useBoolean(false); return ( <> <button onClick={open}>Open Modal</button> {isOpen && ( <div className="modal"> <p>Modal content</p> <button onClick={close}>Close</button> <button onClick={toggle}>Toggle</button> </div> )} </> ); } ``` --- ### useCounter — numeric counter with bounds Provides increment, decrement, set, and reset with optional min/max clamping. ```tsx import { useCounter } from "@reactuses/core"; function CounterDemo() { const [count, setCount, inc, dec, reset] = useCounter(0, 10, 0); // max=10, min=0 return ( <div> <p>Count: {count}</p> <button onClick={() => inc()}>+1</button> <button onClick={() => inc(5)}>+5</button> <button onClick={() => dec()}>-1</button> <button onClick={() => setCount(7)}>Set 7</button> <button onClick={reset}>Reset</button> </div> ); } ``` --- ### useMap — reactive Map data structure Wraps `Map` with reactive state and convenience methods: `set`, `get`, `remove`, `has`, `clear`, `reset`. ```tsx import { useMap } from "@reactuses/core"; function MapDemo() { const { map, set, get, remove, has, clear, size } = useMap<string, number>([ ["apples", 5], ["oranges", 3], ]); return ( <div> <p>Items: {size}</p> <p>Apples: {get("apples")}</p> <button onClick={() => set("bananas", 8)}>Add bananas</button> <button onClick={() => remove("oranges")}>Remove oranges</button> <button onClick={clear}>Clear all</button> <ul> {Array.from(map.entries()).map(([k, v]) => ( <li key={k}>{k}: {v}</li> ))} </ul> </div> ); } ``` --- ### useLocalStorage — persistent state synced to localStorage Persists state across page reloads and syncs changes across browser tabs in real time. ```tsx import { useLocalStorage } from "@reactuses/core"; interface UserPrefs { theme: "light" | "dark"; fontSize: number; } function Settings() { const [prefs, setPrefs] = useLocalStorage<UserPrefs>("user-prefs", { theme: "light", fontSize: 16, }, { listenToStorageChanges: true, // sync across tabs (default: true) onError: (err) => console.error("Storage error:", err), }); return ( <div> <p>Theme: {prefs?.theme}</p> <button onClick={() => setPrefs(p => ({ ...p!, theme: "dark" }))}> Dark Mode </button> <button onClick={() => setPrefs(null)}>Clear</button> </div> ); } ``` --- ### useSessionStorage — session-scoped persistent state Identical API to `useLocalStorage` but backed by `sessionStorage` (cleared when tab closes). ```tsx import { useSessionStorage } from "@reactuses/core"; function WizardStep() { const [step, setStep] = useSessionStorage<number>("wizard-step", 1); return ( <div> <p>Current step: {step}</p> <button onClick={() => setStep(s => (s ?? 1) + 1)}>Next</button> <button onClick={() => setStep(1)}>Restart</button> </div> ); } ``` --- ### useDebounce — debounce a reactive value Returns a debounced copy of a value that only updates after the specified delay has passed without changes. ```tsx import { useDebounce } from "@reactuses/core"; import { useState } from "react"; function SearchInput() { const [query, setQuery] = useState(""); const debouncedQuery = useDebounce(query, 400); // debouncedQuery only changes 400ms after the user stops typing useEffect(() => { if (debouncedQuery) fetchResults(debouncedQuery); }, [debouncedQuery]); return <input value={query} onChange={e => setQuery(e.target.value)} />; } ``` --- ### useDebounceFn — debounce a callback function Returns a `{ run, cancel, flush }` object wrapping a debounced version of the provided function. ```tsx import { useDebounceFn } from "@reactuses/core"; function AutoSave({ onSave }: { onSave: (data: string) => void }) { const { run: save, cancel, flush } = useDebounceFn(onSave, 1000); return ( <div> <textarea onChange={e => save(e.target.value)} /> <button onClick={flush}>Save Now</button> <button onClick={cancel}>Cancel</button> </div> ); } ``` --- ### useThrottle — throttle a reactive value Returns a throttled copy of a value that updates at most once per specified interval. ```tsx import { useThrottle } from "@reactuses/core"; import { useState } from "react"; function ScrollTracker() { const [scrollY, setScrollY] = useState(0); const throttledY = useThrottle(scrollY, 100); // update at most every 100ms useEffect(() => { const handler = () => setScrollY(window.scrollY); window.addEventListener("scroll", handler); return () => window.removeEventListener("scroll", handler); }, []); return <p>Throttled scroll position: {throttledY}px</p>; } ``` --- ### useThrottleFn — throttle a callback function Wraps a function to limit execution frequency, returning `{ run, cancel, flush }`. ```tsx import { useThrottleFn } from "@reactuses/core"; function RateLimitedButton({ onSubmit }: { onSubmit: () => void }) { const { run: submit } = useThrottleFn(onSubmit, 2000); // onSubmit can only be called at most once every 2 seconds return <button onClick={submit}>Submit (throttled 2s)</button>; } ``` --- ### usePrevious — access previous render's value Returns the value from the previous render cycle. Returns `undefined` on first render. ```tsx import { usePrevious } from "@reactuses/core"; function ValueTracker({ value }: { value: number }) { const prev = usePrevious(value); return ( <p> Changed from {prev ?? "—"} → {value} {prev !== undefined && value > prev && " ↑"} {prev !== undefined && value < prev && " ↓"} </p> ); } ``` --- ### useSetState — partial state updates like class components Merges partial updates into existing state, similar to `this.setState` in class components. ```tsx import { useSetState } from "@reactuses/core"; function Form() { const [state, setState] = useSetState({ username: "", email: "", age: 0, loading: false, }); const handleSubmit = async () => { setState({ loading: true }); await submitForm(state); setState({ loading: false }); }; return ( <form> <input value={state.username} onChange={e => setState({ username: e.target.value })} /> <input value={state.email} onChange={e => setState({ email: e.target.value })} /> <button disabled={state.loading} onClick={handleSubmit}> {state.loading ? "Submitting…" : "Submit"} </button> </form> ); } ``` --- ### useDisclosure — manage open/closed UI state Manages open/closed state for modals, drawers, and dropdowns with `open`, `close`, and `toggle` helpers. ```tsx import { useDisclosure } from "@reactuses/core"; function DrawerDemo() { const { isOpen, open, close, toggle } = useDisclosure(false); return ( <> <button onClick={open}>Open Drawer</button> <div className={`drawer ${isOpen ? "drawer--open" : ""}`}> <button onClick={close}>✕ Close</button> <p>Drawer content here</p> </div> </> ); } ``` --- ### useCycleList — cycle through a list of values Steps through an array of values forward and backward, with the current value and navigation functions. ```tsx import { useCycleList } from "@reactuses/core"; const THEMES = ["light", "dark", "high-contrast", "solarized"] as const; function ThemeSwitcher() { const { state: theme, next, prev } = useCycleList(THEMES); return ( <div> <p>Current theme: {theme}</p> <button onClick={prev}>← Prev</button> <button onClick={next}>Next →</button> </div> ); } ``` --- ### useLatest — always-current ref for callbacks Returns a `MutableRefObject` that always holds the latest value of its argument without causing re-renders. Use inside `useCallback`/`useEffect` to avoid stale closures. ```tsx import { useLatest } from "@reactuses/core"; import { useEffect } from "react"; function Logger({ onData }: { onData: (v: string) => void }) { const onDataRef = useLatest(onData); useEffect(() => { const id = setInterval(() => { // Always calls the latest onData without adding it to deps onDataRef.current(new Date().toISOString()); }, 1000); return () => clearInterval(id); }, []); // no re-subscription when onData changes return <p>Logging every second…</p>; } ``` --- ### useEvent — stable callback reference Creates a stable function wrapper whose identity never changes between renders while always invoking the latest version of the provided function. ```tsx import { useEvent } from "@reactuses/core"; function ChatInput({ onSend }: { onSend: (msg: string) => void }) { const [text, setText] = useState(""); // stableSubmit has a stable identity — safe to pass to child components // without breaking memoization const stableSubmit = useEvent(() => { onSend(text); setText(""); }); return ( <div> <input value={text} onChange={e => setText(e.target.value)} /> <SendButton onClick={stableSubmit} /> {/* memo-safe */} </div> ); } ``` --- ## Browser Hooks ### useDarkMode — dark mode with localStorage persistence Reads, writes, and persists the dark/light mode preference. Applies a class to `<html>` and stores the preference in localStorage. ```tsx import { useDarkMode } from "@reactuses/core"; function ThemeToggle() { const [isDark, setIsDark] = useDarkMode(false, { storageKey: "app-dark-mode", selector: "html", attribute: "class", // Add this script to <head> to prevent flash of wrong theme on SSR: // localStorage.getItem("app-dark-mode") === "dark" // ? document.documentElement.classList.add("dark") // : document.documentElement.classList.remove("dark") }); return ( <button onClick={() => setIsDark(d => !d)}> {isDark ? "☀️ Light Mode" : "🌙 Dark Mode"} </button> ); } ``` --- ### useColorMode — multi-theme color mode management Extends dark mode to support arbitrary named color modes (e.g., light, dark, blue, sepia), persisted to storage and applied as classes or attributes on a DOM element. ```tsx import { useColorMode } from "@reactuses/core"; type AppMode = "light" | "dark" | "blue" | "sepia"; function ThemePicker() { const [mode, setMode, cycleMode] = useColorMode<AppMode>({ modes: ["light", "dark", "blue", "sepia"], defaultValue: "light", storageKey: "app-color-mode", selector: "html", attribute: "class", }); return ( <div> <p>Current: {mode}</p> <button onClick={cycleMode}>Cycle Mode</button> {(["light", "dark", "blue", "sepia"] as AppMode[]).map(m => ( <button key={m} onClick={() => setMode(m)} style={{ fontWeight: mode === m ? "bold" : "normal" }}> {m} </button> ))} </div> ); } ``` --- ### useMediaQuery — reactive CSS media query Tracks whether a CSS media query currently matches, re-evaluating on viewport changes. Accepts an optional default value for SSR hydration safety. ```tsx import { useMediaQuery } from "@reactuses/core"; function ResponsiveLayout() { const isMobile = useMediaQuery("(max-width: 767px)", false); const isTablet = useMediaQuery("(min-width: 768px) and (max-width: 1023px)", false); const prefersReducedMotion = useMediaQuery("(prefers-reduced-motion: reduce)", false); return ( <div> {isMobile && <MobileNav />} {isTablet && <TabletNav />} {!isMobile && !isTablet && <DesktopNav />} <p>Animations: {prefersReducedMotion ? "disabled" : "enabled"}</p> </div> ); } ``` --- ### useClipboard — read and write clipboard Provides the current clipboard text and an async `copy` function. Updates automatically when the user copies/cuts or the window regains focus. ```tsx import { useClipboard } from "@reactuses/core"; function ClipboardDemo() { const [clipboardText, copy] = useClipboard(); const handleCopy = async () => { try { await copy("Hello from ReactUse!"); alert("Copied!"); } catch (err) { console.error("Copy failed:", err); } }; return ( <div> <p>Clipboard: {clipboardText || "(empty)"}</p> <button onClick={handleCopy}>Copy greeting</button> </div> ); } ``` --- ### useCookie — reactive cookie management Reads, updates, and deletes a browser cookie, returning its current value and a refresh function for forcing a re-read. ```tsx import { useCookie } from "@reactuses/core"; function SessionBanner() { const [token, setToken, refresh] = useCookie("session-token", { expires: 7, // 7 days secure: true, sameSite: "Strict", }, ""); const logout = () => setToken(undefined); // deletes the cookie return token ? <button onClick={logout}>Logout ({token.slice(0, 8)}…)</button> : <p>Not logged in <button onClick={refresh}>Refresh</button></p>; } ``` --- ### useWindowSize — reactive window dimensions Tracks the browser window's width and height, updating on resize with optimized re-renders via getter properties. ```tsx import { useWindowSize } from "@reactuses/core"; function CanvasBackground() { const { width, height } = useWindowSize(); return ( <canvas width={width} height={height} style={{ position: "fixed", top: 0, left: 0, zIndex: -1 }} /> ); } ``` --- ### useWindowScroll — reactive scroll position Tracks the window's current scroll position (`x`, `y`) and provides a `scrollTo` helper. ```tsx import { useWindowScroll } from "@reactuses/core"; function ScrollProgress() { const { y, scrollTo } = useWindowScroll(); const docHeight = document.documentElement.scrollHeight - window.innerHeight; const progress = docHeight > 0 ? (y / docHeight) * 100 : 0; return ( <> <div style={{ width: `${progress}%`, height: 4, background: "blue", position: "fixed", top: 0 }} /> {y > 300 && ( <button style={{ position: "fixed", bottom: 20, right: 20 }} onClick={() => scrollTo({ top: 0, behavior: "smooth" })} > ↑ Top </button> )} </> ); } ``` --- ### useNetwork — network connection status Reports the browser's online/offline state and detailed connection information (`effectiveType`, `downlink`, `rtt`, `saveData`). ```tsx import { useNetwork } from "@reactuses/core"; function NetworkIndicator() { const { online, effectiveType, downlink, saveData, since } = useNetwork(); if (!online) return <div className="banner error">You are offline (since {since?.toLocaleTimeString()})</div>; return ( <div className="banner info"> Online · {effectiveType?.toUpperCase()} · {downlink}Mbps {saveData && " · Data Saver ON"} </div> ); } ``` --- ### useGeolocation — GPS/location tracking Wraps the Geolocation API, returning `{ coords, locatedAt, error, isSupported }` with automatic updates. ```tsx import { useGeolocation } from "@reactuses/core"; function LocationDisplay() { const { coords, locatedAt, error, isSupported } = useGeolocation({ enableHighAccuracy: true, timeout: 5000, maximumAge: 10000, }); if (!isSupported) return <p>Geolocation not supported.</p>; if (error) return <p>Error: {error.message}</p>; if (!coords) return <p>Locating…</p>; return ( <p> {coords.latitude.toFixed(5)}, {coords.longitude.toFixed(5)} (±{coords.accuracy}m) — {locatedAt?.toLocaleTimeString()} </p> ); } ``` --- ### useIdle — detect user inactivity Detects when the user has been idle for a specified duration, watching mouse, keyboard, pointer, and visibility events. ```tsx import { useIdle } from "@reactuses/core"; function AutoLogout() { const isIdle = useIdle(5 * 60 * 1000); // 5 minutes useEffect(() => { if (isIdle) { alert("Session expired due to inactivity."); logout(); } }, [isIdle]); return <p>Status: {isIdle ? "Idle — logging out…" : "Active"}</p>; } ``` --- ### useTitle — set the document title Sets `document.title` reactively and optionally restores the previous title on unmount. ```tsx import { useTitle } from "@reactuses/core"; function ProductPage({ product }: { product: { name: string } }) { useTitle(`${product.name} — My Store`, { restoreOnUnmount: true, // restore previous title when component unmounts }); return <h1>{product.name}</h1>; } ``` --- ### useFavicon — dynamically change the page favicon Swaps the `<link rel="icon">` href reactively. ```tsx import { useFavicon } from "@reactuses/core"; function NotificationBadge({ unread }: { unread: number }) { useFavicon(unread > 0 ? "/favicon-badge.ico" : "/favicon.ico"); return <span>{unread} unread</span>; } ``` --- ### useFullscreen — element fullscreen API Enters/exits fullscreen for any element, tracking the current state and checking browser support. ```tsx import { useFullscreen } from "@reactuses/core"; import { useRef } from "react"; function VideoPlayer() { const videoRef = useRef<HTMLVideoElement>(null); const [isFullscreen, { enterFullscreen, exitFullscreen, isEnabled }] = useFullscreen(videoRef, { onEnter: () => console.log("Entered fullscreen"), onExit: () => console.log("Exited fullscreen"), }); return ( <div> <video ref={videoRef} src="/video.mp4" controls /> {isEnabled && ( <button onClick={isFullscreen ? exitFullscreen : enterFullscreen}> {isFullscreen ? "Exit Fullscreen" : "Go Fullscreen"} </button> )} </div> ); } ``` --- ### useBroadcastChannel — cross-tab messaging Creates a BroadcastChannel for sending and receiving messages across browser tabs/windows of the same origin. ```tsx import { useBroadcastChannel } from "@reactuses/core"; type Message = { type: "LOGOUT" | "REFRESH_TOKEN"; payload?: string }; function TabSync() { const { isSupported, data, post, isClosed } = useBroadcastChannel<Message, Message>({ name: "app-sync", }); useEffect(() => { if (data?.type === "LOGOUT") performLogout(); if (data?.type === "REFRESH_TOKEN") setToken(data.payload!); }, [data]); if (!isSupported) return null; return ( <button onClick={() => post({ type: "LOGOUT" })}> Logout All Tabs </button> ); } ``` --- ### useEventSource — Server-Sent Events (SSE) Connects to an SSE endpoint and delivers the latest message data, managing connection lifecycle automatically. ```tsx import { useEventSource } from "@reactuses/core"; function LiveFeed() { const { data, status, close } = useEventSource("/api/events", { withCredentials: true, }); return ( <div> <p>Connection: {status}</p> <p>Latest event: {data}</p> <button onClick={close}>Disconnect</button> </div> ); } ``` --- ### usePermission — query browser permission status Queries the Permissions API for a given permission name, returning its current state (`granted`, `denied`, or `prompt`). ```tsx import { usePermission } from "@reactuses/core"; function CameraButton() { const cameraState = usePermission("camera"); if (cameraState === "denied") return <p>Camera access denied.</p>; return ( <button onClick={() => navigator.mediaDevices.getUserMedia({ video: true })} > {cameraState === "granted" ? "Open Camera" : "Request Camera Access"} </button> ); } ``` --- ## Element Hooks ### useHover — detect element hover state Returns `true` while the pointer is over the target element, using `pointerenter`/`pointerleave` events. ```tsx import { useHover } from "@reactuses/core"; import { useRef } from "react"; function HoverCard() { const ref = useRef<HTMLDivElement>(null); const isHovered = useHover(ref); return ( <div ref={ref} style={{ padding: 20, background: isHovered ? "#e0f0ff" : "#f5f5f5", transition: "background 0.2s", }} > {isHovered ? "Hovering!" : "Hover over me"} </div> ); } ``` --- ### useClickOutside — detect clicks outside an element Calls a handler when a click or touch occurs outside the target element. Accepts an optional `enabled` flag. ```tsx import { useClickOutside } from "@reactuses/core"; import { useRef, useState } from "react"; function Dropdown() { const [open, setOpen] = useState(false); const ref = useRef<HTMLDivElement>(null); useClickOutside(ref, () => setOpen(false), open); return ( <div ref={ref} style={{ position: "relative" }}> <button onClick={() => setOpen(o => !o)}>Menu ▾</button> {open && ( <ul style={{ position: "absolute", top: "100%", left: 0, background: "white", border: "1px solid #ccc" }}> <li>Item 1</li> <li>Item 2</li> <li>Item 3</li> </ul> )} </div> ); } ``` --- ### useElementSize — track element dimensions Uses `ResizeObserver` to reactively report an element's `width` and `height`. ```tsx import { useElementSize } from "@reactuses/core"; import { useRef } from "react"; function ResponsiveChart() { const containerRef = useRef<HTMLDivElement>(null); const [width, height] = useElementSize(containerRef); return ( <div ref={containerRef} style={{ width: "100%", height: 400 }}> <svg width={width} height={height}> <rect x={0} y={0} width={width} height={height} fill="#eef" /> <text x={width / 2} y={height / 2} textAnchor="middle"> {width}×{height} </text> </svg> </div> ); } ``` --- ### useElementBounding — reactive bounding rect Returns the element's `DOMRect` properties (`top`, `left`, `right`, `bottom`, `width`, `height`, `x`, `y`), updated on scroll and resize. ```tsx import { useElementBounding } from "@reactuses/core"; import { useRef } from "react"; function TooltipAnchor() { const ref = useRef<HTMLButtonElement>(null); const { top, left, width } = useElementBounding(ref); return ( <> <button ref={ref}>Hover for tooltip</button> <div style={{ position: "fixed", top: top - 40, left: left + width / 2, transform: "translateX(-50%)", background: "black", color: "white", padding: "4px 8px", borderRadius: 4, pointerEvents: "none", }} > Tooltip! </div> </> ); } ``` --- ### useElementVisibility — is element visible in viewport Returns a boolean indicating whether the element is currently visible in the viewport using `IntersectionObserver`. ```tsx import { useElementVisibility } from "@reactuses/core"; import { useRef } from "react"; function LazySection() { const ref = useRef<HTMLDivElement>(null); const isVisible = useElementVisibility(ref); return ( <div ref={ref} style={{ minHeight: 200, background: isVisible ? "#d4edda" : "#f8d7da" }}> {isVisible ? "✓ Visible — content loaded" : "Not visible yet"} </div> ); } ``` --- ### useDraggable — draggable elements Makes an element draggable using pointer events, supporting handles, containers, and positional constraints. ```tsx import { useDraggable } from "@reactuses/core"; import { useRef } from "react"; function DraggablePanel() { const panelRef = useRef<HTMLDivElement>(null); const containerRef = useRef<HTMLDivElement>(null); const [x, y, isDragging, setPosition] = useDraggable(panelRef, { containerElement: containerRef, initialValue: { x: 50, y: 50 }, onStart: (pos) => { console.log("drag started", pos); return true; }, onEnd: (pos) => console.log("drag ended", pos), }); return ( <div ref={containerRef} style={{ position: "relative", width: 600, height: 400, border: "1px solid #ccc" }}> <div ref={panelRef} style={{ position: "absolute", left: x, top: y, width: 150, height: 100, background: isDragging ? "#bee3f8" : "#ebf8ff", cursor: isDragging ? "grabbing" : "grab", border: "2px solid #63b3ed", borderRadius: 8, }} > Drag me </div> </div> ); } ``` --- ### useMouse — reactive cursor position Tracks the cursor's position (`x`, `y`) relative to the page and optionally a specific element. ```tsx import { useMouse } from "@reactuses/core"; function MouseTracker() { const { x, y, sourceType } = useMouse(); return ( <div style={{ height: "100vh" }}> <p>Cursor: ({x}, {y}) via {sourceType}</p> <div style={{ position: "fixed", left: x + 12, top: y + 12, pointerEvents: "none", background: "rgba(0,0,0,0.7)", color: "white", padding: "2px 6px", borderRadius: 4, fontSize: 12, }} > {x}, {y} </div> </div> ); } ``` --- ### useScroll — reactive scroll position of an element Tracks the scroll `x` and `y` of any scrollable element (or the window). ```tsx import { useScroll } from "@reactuses/core"; import { useRef } from "react"; function ScrollableList() { const listRef = useRef<HTMLUListElement>(null); const { x, y, arrivedState, directions } = useScroll(listRef); return ( <div> <p> Scroll: {Math.round(y)}px {arrivedState.bottom && " — reached bottom!"} {directions.top && " ↑ scrolling up"} </p> <ul ref={listRef} style={{ height: 200, overflowY: "auto" }}> {Array.from({ length: 50 }, (_, i) => <li key={i}>Item {i + 1}</li>)} </ul> </div> ); } ``` --- ### useScrollIntoView — programmatically scroll element into view Returns a function to smoothly scroll a target element into the viewport. ```tsx import { useScrollIntoView } from "@reactuses/core"; import { useRef } from "react"; function JumpToSection() { const sectionRef = useRef<HTMLElement>(null); const scrollIntoView = useScrollIntoView(sectionRef, { behavior: "smooth", block: "start", }); return ( <> <button onClick={scrollIntoView}>Jump to Section</button> <div style={{ height: "100vh" }} /> <section ref={sectionRef}> <h2>Target Section</h2> </section> </> ); } ``` --- ### useIntersectionObserver — observe element intersection Low-level `IntersectionObserver` hook that fires a callback with intersection entries when the target enters or exits a root. ```tsx import { useIntersectionObserver } from "@reactuses/core"; import { useRef, useState } from "react"; function AnimatedCard() { const ref = useRef<HTMLDivElement>(null); const [visible, setVisible] = useState(false); useIntersectionObserver( ref, ([entry]) => setVisible(entry.isIntersecting), { threshold: 0.3, rootMargin: "0px 0px -50px 0px" } ); return ( <div ref={ref} style={{ opacity: visible ? 1 : 0, transform: visible ? "translateY(0)" : "translateY(40px)", transition: "opacity 0.5s, transform 0.5s", padding: 40, background: "#f0f4ff", margin: "20px 0", }} > Fades in on scroll </div> ); } ``` --- ### useResizeObserver — observe element size changes Fires a callback whenever the target element's dimensions change using `ResizeObserver`. ```tsx import { useResizeObserver } from "@reactuses/core"; import { useRef, useState } from "react"; function TextAreaAutoExpand() { const ref = useRef<HTMLTextAreaElement>(null); const [size, setSize] = useState({ width: 0, height: 0 }); useResizeObserver(ref, (entries) => { const { width, height } = entries[0].contentRect; setSize({ width, height }); }); return ( <> <textarea ref={ref} style={{ width: "100%", resize: "both" }} rows={4} /> <p>Current size: {Math.round(size.width)}×{Math.round(size.height)}</p> </> ); } ``` --- ### useMutationObserver — observe DOM mutations Fires a callback when the target element's children, attributes, or text content change. ```tsx import { useMutationObserver } from "@reactuses/core"; import { useRef, useState } from "react"; function DOMWatcher() { const ref = useRef<HTMLDivElement>(null); const [mutationCount, setMutationCount] = useState(0); useMutationObserver( ref, (mutations) => setMutationCount(c => c + mutations.length), { childList: true, subtree: true, attributes: true } ); return ( <div> <div ref={ref} id="watched"> <p>Watched element</p> </div> <p>Mutations detected: {mutationCount}</p> <button onClick={() => document.getElementById("watched")!.appendChild(document.createElement("span"))}> Add node </button> </div> ); } ``` --- ### useLongPress — detect long press gestures Attaches handlers for detecting long presses on both mouse and touch devices. Returns event handler props to spread onto an element. ```tsx import { useLongPress } from "@reactuses/core"; function ContextButton() { const handlers = useLongPress( (e) => { console.log("Long pressed!", e); showContextMenu(e); }, { delay: 500, isPreventDefault: true } ); return ( <button {...handlers} onContextMenu={e => e.preventDefault()}> Hold for context menu </button> ); } ``` --- ### useDropZone — file/element drop zone Tracks drag-over state and fires callbacks on file drop for a target element. ```tsx import { useDropZone } from "@reactuses/core"; import { useRef, useState } from "react"; function FileDropZone() { const dropRef = useRef<HTMLDivElement>(null); const [files, setFiles] = useState<File[]>([]); const { isOverDropZone } = useDropZone(dropRef, { onDrop: (droppedFiles) => setFiles(droppedFiles ?? []), dataTypes: ["image/png", "image/jpeg", "application/pdf"], }); return ( <div ref={dropRef} style={{ padding: 40, border: `2px dashed ${isOverDropZone ? "#3182ce" : "#a0aec0"}`, background: isOverDropZone ? "#ebf8ff" : "#f7fafc", textAlign: "center", }} > {isOverDropZone ? "Release to drop" : "Drag files here"} <ul>{files.map(f => <li key={f.name}>{f.name} ({(f.size / 1024).toFixed(1)} KB)</li>)}</ul> </div> ); } ``` --- ### useActiveElement — track focused element Returns the currently focused `document.activeElement`, updating as focus moves. ```tsx import { useActiveElement } from "@reactuses/core"; function FocusTracker() { const activeEl = useActiveElement(); return ( <div> <p>Focused: {activeEl?.tagName?.toLowerCase() ?? "none"} — {(activeEl as HTMLInputElement)?.name}</p> <input name="username" placeholder="Username" /> <input name="password" type="password" placeholder="Password" /> </div> ); } ``` --- ### useTextSelection — get selected text Returns the current text `selection` and `text` string whenever the user selects text in the document. ```tsx import { useTextSelection } from "@reactuses/core"; function SelectionHighlighter() { const { text, rects } = useTextSelection(); return ( <div> <p> ReactUse makes it easy to build interactive React applications with well-designed, production-ready hooks. </p> {text && ( <div style={{ position: "fixed", bottom: 20, right: 20, background: "#2d3748", color: "white", padding: "8px 12px", borderRadius: 6 }}> Selected: "{text}" </div> )} </div> ); } ``` --- ## Effect Hooks ### useMount — run effect on mount only Executes a callback exactly once when the component mounts. Cleaner alternative to `useEffect(fn, [])`. ```tsx import { useMount } from "@reactuses/core"; function DataLoader({ id }: { id: string }) { const [data, setData] = useState(null); useMount(() => { fetchData(id).then(setData); analytics.track("page_view", { id }); }); return data ? <DataView data={data} /> : <Spinner />; } ``` --- ### useUnmount — run effect on unmount only Executes a cleanup callback when the component is about to unmount. ```tsx import { useUnmount } from "@reactuses/core"; function VideoPlayer({ streamId }: { streamId: string }) { const playerRef = useRef<VideoPlayer | null>(null); useMount(() => { playerRef.current = initPlayer(streamId); }); useUnmount(() => { playerRef.current?.destroy(); console.log("Player destroyed"); }); return <div id="player-container" />; } ``` --- ### useInterval — managed setInterval Wraps `setInterval` with pause/resume controls and automatic cleanup. ```tsx import { useInterval } from "@reactuses/core"; import { useState } from "react"; function Stopwatch() { const [seconds, setSeconds] = useState(0); const { isActive, pause, resume } = useInterval( () => setSeconds(s => s + 1), 1000, { immediate: false } ); return ( <div> <p>{seconds}s</p> <button onClick={isActive.current ? pause : resume}> {isActive.current ? "Pause" : "Start"} </button> <button onClick={() => setSeconds(0)}>Reset</button> </div> ); } ``` --- ### useEventListener — attach DOM event listeners Attaches and cleans up an event listener on any target (window, document, element, or custom EventTarget). ```tsx import { useEventListener } from "@reactuses/core"; import { useState } from "react"; function KeyboardShortcuts() { const [lastKey, setLastKey] = useState(""); useEventListener("keydown", (e: KeyboardEvent) => { if (e.ctrlKey && e.key === "s") { e.preventDefault(); saveDocument(); } setLastKey(e.key); }); return <p>Last key pressed: {lastKey}</p>; } ``` --- ### useAsyncEffect — async-safe useEffect Runs an async function as an effect, handling cleanup gracefully without the "can't update unmounted component" warning. ```tsx import { useAsyncEffect } from "@reactuses/core"; import { useState } from "react"; function UserProfile({ userId }: { userId: string }) { const [user, setUser] = useState(null); const [error, setError] = useState<Error | null>(null); useAsyncEffect( async () => { try { const data = await fetch(`/api/users/${userId}`).then(r => r.json()); setUser(data); } catch (err) { setError(err as Error); } }, async () => { /* cleanup: cancel ongoing requests here */ }, [userId] ); if (error) return <p>Error: {error.message}</p>; if (!user) return <p>Loading…</p>; return <div>{user.name}</div>; } ``` --- ### useDeepCompareEffect — effect with deep equality check Like `useEffect` but performs deep equality comparison on dependencies, preventing unnecessary re-runs when object references change but values are the same. ```tsx import { useDeepCompareEffect } from "@reactuses/core"; function FilteredList({ filters }: { filters: Record<string, string[]> }) { const [results, setResults] = useState([]); // Without deepCompare, this would re-run every render because // `filters` is a new object reference each time useDeepCompareEffect(() => { fetchFilteredData(filters).then(setResults); }, [filters]); return <ul>{results.map(r => <li key={r.id}>{r.name}</li>)}</ul>; } ``` --- ### useRafFn — requestAnimationFrame loop Runs a callback on every animation frame, with start/stop controls for animation loops. ```tsx import { useRafFn } from "@reactuses/core"; import { useRef } from "react"; function ParticleCanvas() { const canvasRef = useRef<HTMLCanvasElement>(null); const angleRef = useRef(0); const [stop, start] = useRafFn((timestamp) => { const ctx = canvasRef.current?.getContext("2d"); if (!ctx) return; ctx.clearRect(0, 0, 400, 400); angleRef.current += 0.02; const x = 200 + Math.cos(angleRef.current) * 100; const y = 200 + Math.sin(angleRef.current) * 100; ctx.beginPath(); ctx.arc(x, y, 10, 0, Math.PI * 2); ctx.fillStyle = "#4299e1"; ctx.fill(); }); return ( <> <canvas ref={canvasRef} width={400} height={400} /> <button onClick={stop}>Stop</button> <button onClick={start}>Start</button> </> ); } ``` --- ### useTimeout — declarative setTimeout Fires a callback once after a delay, with controls to reset or clear it. ```tsx import { useTimeout } from "@reactuses/core"; import { useState } from "react"; function Notification({ message }: { message: string }) { const [visible, setVisible] = useState(true); const { clear, reset } = useTimeout(() => setVisible(false), 5000); if (!visible) return null; return ( <div className="notification"> <p>{message}</p> <button onClick={() => { clear(); setVisible(false); }}>Dismiss</button> <button onClick={reset}>Reset Timer</button> </div> ); } ``` --- ### useUpdateEffect — skip first render Like `useEffect` but skips execution on the initial mount, firing only on subsequent updates. ```tsx import { useUpdateEffect } from "@reactuses/core"; function SearchResults({ query }: { query: string }) { const [results, setResults] = useState([]); // Only fires when `query` changes AFTER the first render useUpdateEffect(() => { analytics.track("search", { query }); search(query).then(setResults); }, [query]); return <ul>{results.map(r => <li key={r.id}>{r.name}</li>)}</ul>; } ``` --- ### useMergedRefs — merge multiple refs Combines multiple refs (callback refs and ref objects) into a single ref to attach to one element. ```tsx import { useMergedRefs } from "@reactuses/core"; import { forwardRef, useRef } from "react"; const FancyInput = forwardRef<HTMLInputElement, React.InputHTMLAttributes<HTMLInputElement>>( function FancyInput(props, forwardedRef) { const localRef = useRef<HTMLInputElement>(null); const mergedRef = useMergedRefs(localRef, forwardedRef); useEffect(() => { // Access via localRef without needing forwardedRef localRef.current?.focus(); }, []); return <input ref={mergedRef} {...props} />; } ); ``` --- ### useCssVar — reactive CSS custom properties Reads and writes a CSS custom property on a target element (defaults to `:root`). ```tsx import { useCssVar } from "@reactuses/core"; function ThemeEditor() { const [primaryColor, setPrimaryColor] = useCssVar("--color-primary", "#3182ce"); const [spacing, setSpacing] = useCssVar("--spacing-base", "8px"); return ( <div> <label> Primary Color: <input type="color" value={primaryColor} onChange={e => setPrimaryColor(e.target.value)} /> </label> <label> Base Spacing: <input type="range" min={4} max={24} value={parseInt(spacing)} onChange={e => setSpacing(`${e.target.value}px`)} /> </label> </div> ); } ``` --- ### useCountDown — countdown timer Counts down from a target date/timestamp, returning the remaining time broken into days, hours, minutes, and seconds. ```tsx import { useCountDown } from "@reactuses/core"; function SaleTimer() { const saleEnd = new Date("2025-12-31T23:59:59").getTime(); const [timeLeft, formattedParts] = useCountDown(saleEnd); if (timeLeft <= 0) return <p>Sale has ended!</p>; const { days, hours, minutes, seconds } = formattedParts; return ( <div className="countdown"> <span>{days}d</span> <span>{hours}h</span>{" "} <span>{minutes}m</span> <span>{seconds}s</span> </div> ); } ``` --- ## MCP Integration ### @reactuses/mcp — AI-powered hook discovery The `@reactuses/mcp` package exposes three MCP tools (`get-hook-details`, `list-hooks`, `search-hooks`) so that AI assistants can look up hooks, their signatures, and documentation at generation time. ```json // Add to your MCP client configuration (e.g., Claude Desktop config.json) { "mcpServers": { "@reactuses/mcp": { "command": "npx", "args": ["-y", "@reactuses/mcp@latest"], "type": "stdio" } } } ``` ```typescript // MCP tools exposed by the server: // 1. list-hooks — list all hooks or filter by category // Input: { category?: "browser" | "state" | "element" | "effect" } // Returns: array of { name, description, category } // 2. get-hook-details — full signature + description for one hook // Input: { name: "useLocalStorage" } // Returns: { name, description, parameters, returns, example } // 3. search-hooks — fuzzy-search hooks by keyword // Input: { query: "clipboard", category?: "browser" } // Returns: array of matching hooks with descriptions ``` --- ## Summary ReactUse (`@reactuses/core`) addresses the full spectrum of day-to-day React development needs through a unified, tree-shakable hooks library. State management is covered by hooks like `useLocalStorage`, `useMap`, `useBoolean`, `useCounter`, `useDebounce`, and `useThrottle`. Browser integration spans clipboard, cookies, dark mode, fullscreen, geolocation, idle detection, network status, media queries, permissions, and cross-tab messaging via `useBroadcastChannel`. DOM interactions are handled by `useDraggable`, `useHover`, `useClickOutside`, `useIntersectionObserver`, `useResizeObserver`, `useMutationObserver`, and many more. Effect utilities like `useAsyncEffect`, `useDeepCompareEffect`, `useRafFn`, and `useInterval` round out the collection. The library integrates cleanly into any React 19 project — including SSR frameworks such as Next.js and Remix — by providing safe default values for server rendering and stable internal references that prevent infinite re-render loops. Hooks accept `BasicTarget` values (refs, raw elements, or lazy accessor functions), making them composable inside component trees and custom hooks alike. For teams using AI coding assistants, the companion `@reactuses/mcp` package exposes all hook metadata through the Model Context Protocol, enabling context-aware code generation directly from the hook documentation.