useHover
A React hook that tracks whether an element is being hovered.
Installation
npx shadcn@latest add https://r.fiberui.com/r/hooks/use-hover.jsonFeatures
- Simple API - Just pass a ref and get a boolean.
- SSR Safe - Works seamlessly with server-side rendering.
- Performance - Uses
mouseenterandmouseleaveevents for efficiency (no bubbling).
Source Code
View the full hook implementation in the Hook Source Code section below.
Interactive Example
Hover Detection
Hover over the box below to see the state change.
Hover me
(false)
"use client";
import { useRef } from "react";
import { useHover } from "@repo/hooks/dom/use-hover";
import { MousePointer2 } from "lucide-react";
export const Example1 = () => {
const ref = useRef<HTMLDivElement>(null);
const isHovered = useHover(ref);
return (
<div className="flex w-full max-w-md flex-col gap-4">
<div className="flex items-center gap-2">
<MousePointer2 className="text-primary h-5 w-5" />
<h3 className="font-semibold">Hover Detection</h3>
</div>
<p className="text-muted-foreground text-sm">
Hover over the box below to see the state change.
</p>
<div
ref={ref}
className={`flex h-32 items-center justify-center rounded-lg border-2 transition-all duration-200 ${
isHovered
? "border-primary bg-primary/10 text-primary scale-105 shadow-md"
: "border-muted bg-muted/20 text-muted-foreground"
}`}
>
<div className="text-center">
<p className="text-lg font-bold">
{isHovered ? "Hovered!" : "Hover me"}
</p>
<p className="text-xs opacity-70">
{isHovered ? "(true)" : "(false)"}
</p>
</div>
</div>
</div>
);
};
const ref = useRef(null);
const isHovered = useHover(ref);Tooltip Interaction
Triggers visibility of a floating element.
Tooltip Interaction
This tooltip appears when the button is hovered!
The tooltip visibility is controlled purely by the useHover hook.
"use client";
import { useRef } from "react";
import { useHover } from "@repo/hooks/dom/use-hover";
import { Info } from "lucide-react";
export const Example2 = () => {
const triggerRef = useRef<HTMLButtonElement>(null);
const isHovered = useHover(triggerRef);
return (
<div className="flex w-full max-w-md flex-col items-center gap-4">
<h3 className="font-semibold">Tooltip Interaction</h3>
<div className="relative">
<button
ref={triggerRef}
className="hover:bg-muted flex items-center gap-2 rounded-full border px-4 py-2 text-sm font-medium transition-colors"
>
<Info className="h-4 w-4" />
Hover me for info
</button>
<div
className={`bg-popover text-popover-foreground absolute left-1/2 top-full mt-2 w-48 -translate-x-1/2 rounded-lg border p-2 text-center text-xs shadow-md transition-all duration-200 ${
isHovered
? "visible translate-y-0 opacity-100"
: "invisible -translate-y-2 opacity-0"
}`}
>
<p>This tooltip appears when the button is hovered!</p>
<div className="bg-popover absolute -top-1 left-1/2 h-2 w-2 -translate-x-1/2 rotate-45 border-l border-t" />
</div>
</div>
<p className="text-muted-foreground text-xs">
The tooltip visibility is controlled purely by the{" "}
<code>useHover</code> hook.
</p>
</div>
);
};
const isHovered = useHover(triggerRef); API Reference
Signature
const isHovered = useHover(ref);Parameters
| Parameter | Type | Description |
|---|---|---|
ref | RefObject<HTMLElement> | React ref to the target element. |
Returns
| Type | Description |
|---|---|
boolean | true if the element is hovered, false otherwise. |
Hook Source Code
import { RefObject, useState, useCallback, useRef, useEffect } from "react";
import {
useEventListener,
EventListenerOptions,
} from "@repo/hooks/dom/use-event-listener";
/**
* useHover - Track hover state on an element
*
* @param ref - React ref pointing to the target element
* @param callback - Optional callback when hover state changes
* @param options - Optional event listener options
* @returns boolean indicating if the element is currently hovered
*
* @example
* const ref = useRef<HTMLDivElement>(null);
* const isHovered = useHover(ref);
*
* // With callback
* const isHovered = useHover(ref, (hovered) => console.log(hovered));
*/
export function useHover<T extends HTMLElement>(
ref: RefObject<T | null>,
callback?: (isHovered: boolean, event: MouseEvent) => void,
options?: EventListenerOptions,
): boolean {
const [isHovered, setIsHovered] = useState(false);
const callbackRef = useRef(callback);
useEffect(() => {
callbackRef.current = callback;
}, [callback]);
const handleMouseEnter = useCallback((event: MouseEvent) => {
setIsHovered(true);
callbackRef.current?.(true, event);
}, []);
const handleMouseLeave = useCallback((event: MouseEvent) => {
setIsHovered(false);
callbackRef.current?.(false, event);
}, []);
useEventListener("mouseenter", handleMouseEnter, ref, options);
useEventListener("mouseleave", handleMouseLeave, ref, options);
return isHovered;
}