useHover
UI/Interaction
A React hook that tracks hover state on elements, with support for both internal and external refs.
Demo
Hover over these cards to see the effect:
Card with Hook-Created Ref
Not hovering
Card with External Ref
Not hovering
Installation
npm install @thibault.sh/hooks
Usage
Method 1: Hook-created ref
import { useHover } from '@thibault.sh/hooks/useHover';
function HoverCard() {
const [cardRef, isHovered] = useHover<HTMLDivElement>();
return (
<div
ref={cardRef}
className={`card ${isHovered ? 'elevated' : ''}`}
>
{isHovered ? 'Hovering!' : 'Hover me'}
</div>
);
}
Method 2: External ref
import { useHover } from '@thibault.sh/hooks/useHover';
import { useRef } from 'react';
function HoverCard() {
const cardRef = useRef<HTMLDivElement>(null);
const [_, isHovered] = useHover(cardRef);
return (
<div
ref={cardRef}
className={`card ${isHovered ? 'elevated' : ''}`}
>
{isHovered ? 'Hovering!' : 'Hover me'}
</div>
);
}
API
Parameters
[ref]
RefObject<T>Optional React ref object for the element to monitor
Returns
{[RefObject<T>, boolean]} Tuple containing:
Usage Notes:
- • You can either let the hook create a ref or provide your own
- • When providing your own ref, you can ignore the returned ref using _
- • The hook works with any HTML element type via generics
Features
- ✓Flexible Usage
Works with both internal and external refs
- ✓Type Safety
Full TypeScript support with element type inference
- ✓Performance
Uses native mouseenter/mouseleave events for efficiency
- ✓Cleanup
Automatically removes event listeners on unmount
Tooltip Example
import { useHover } from '@thibault.sh/hooks';
import { useRef } from 'react';
function HoverTooltip() {
const [tooltipTrigger, isHovered] = useHover<HTMLButtonElement>();
return (
<div className="relative inline-block">
<button
ref={tooltipTrigger}
className="px-4 py-2 bg-blue-500 text-white rounded"
>
Hover me
</button>
{isHovered && (
<div className="absolute bottom-full left-1/2 transform -translate-x-1/2 mb-2 px-3 py-1 bg-black text-white text-sm rounded">
Tooltip content
</div>
)}
</div>
);
}
Image Gallery Example
import { useHover } from '@thibault.sh/hooks';
interface ImageCardProps {
src: string;
alt: string;
description: string;
}
function ImageCard({ src, alt, description }: ImageCardProps) {
const [imageRef, isHovered] = useHover<HTMLDivElement>();
return (
<div
ref={imageRef}
className="relative overflow-hidden rounded-lg"
>
<img
src={src}
alt={alt}
className={`w-full transition-transform duration-300 ${
isHovered ? 'scale-110' : 'scale-100'
}`}
/>
{isHovered && (
<div className="absolute inset-0 bg-black bg-opacity-50 transition-opacity duration-300">
<div className="absolute bottom-0 left-0 right-0 p-4 text-white">
<p>{description}</p>
</div>
</div>
)}
</div>
);
}
function ImageGallery() {
const images = [
{
src: '/image1.jpg',
alt: 'Nature',
description: 'Beautiful landscape'
},
// ... more images
];
return (
<div className="grid grid-cols-3 gap-4">
{images.map((image, index) => (
<ImageCard key={index} {...image} />
))}
</div>
);
}