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>
  );
}