useHover

Handles pointer hover interactions for an element. Normalizes behavior across browsers and platforms, and ignores emulated mouse events on touch devices.

installyarn add @react-aria/interactions
version3.4.0
usageimport {useHover} from '@react-aria/interactions'

API#


useHover( (props: HoverProps )): HoverResult

Features#


useHover handles hover interactions for an element. A hover interaction begins when a user moves their pointer over an element, and ends when they move their pointer off of the element.

  • Uses pointer events where available, with fallbacks to mouse and touch events
  • Ignores emulated mouse events in mobile browsers

useHover is similar to the :hover pseudo class in CSS, but :hover is problematic on touch devices due to mouse emulation in mobile browsers. Depending on the browser and device, :hover may never apply, or may apply continuously until the user touches another element. useHover only applies when the pointer is truly capable of hovering, and emulated mouse events are ignored.

Usage#


useHover returns props that you should spread onto the target element:

NameTypeDescription
hoverPropsHTMLAttributes<HTMLElement>Props to spread on the target element.
isHoveredboolean

useHover supports the following event handlers:

NameTypeDescription
onHoverStart( (e: HoverEvent )) => voidHandler that is called when a hover interaction starts.
onHoverEnd( (e: HoverEvent )) => voidHandler that is called when a hover interaction ends.
onHoverChange( (isHovering: boolean )) => voidHandler that is called when the hover state changes.

Each of these handlers is fired with a HoverEvent, which exposes information about the target and the type of event that triggered the interaction.

NameTypeDescription
type'hoverstart''hoverend'The type of hover event being fired.
pointerType'mouse''pen'The pointer type that triggered the hover event.
targetHTMLElementThe target element of the hover event.

Accessibility#


Hover interactions should never be the only way to interact with an element because they are not supported across all devices. Alternative interactions should be provided on touch devices, for example a long press or an explicit button to tap.

In addition, even on devices with hover support, users may be using a keyboard or screen reader to navigate your app, which also do not trigger hover events. Hover interactions should be paired with focus events in order to expose the content to keyboard users.

Example#


This example shows a simple target that handles hover events with useHover and logs them to a list below. It also uses the isHovered state to adjust the background color when the target is hovered.

function Example() {
  let [events, setEvents] = React.useState([]);
  let {hoverProps, isHovered} = useHover({
    onHoverStart: (e) =>
      setEvents((events) => [...events, `hover start with ${e.pointerType}`]),
    onHoverEnd: (e) =>
      setEvents((events) => [...events, `hover end with ${e.pointerType}`])
  });

  return (
    <>
      <div
        {...hoverProps}
        style={{
          background: isHovered ? 'darkgreen' : 'green',
          color: 'white',
          display: 'inline-block',
          padding: 4,
          cursor: 'pointer'
        }}
        role="button"
        tabIndex={0}>
        Hover me!
      </div>
      <ul
        style={{
          maxHeight: '200px',
          overflow: 'auto'
        }}>
        {events.map((e, i) => (
          <li key={i}>{e}</li>
        ))}
      </ul>
    </>
  );
}
function Example() {
  let [events, setEvents] = React.useState([]);
  let {hoverProps, isHovered} = useHover({
    onHoverStart: (e) =>
      setEvents((events) => [
        ...events,
        `hover start with ${e.pointerType}`
      ]),
    onHoverEnd: (e) =>
      setEvents((events) => [
        ...events,
        `hover end with ${e.pointerType}`
      ])
  });

  return (
    <>
      <div
        {...hoverProps}
        style={{
          background: isHovered ? 'darkgreen' : 'green',
          color: 'white',
          display: 'inline-block',
          padding: 4,
          cursor: 'pointer'
        }}
        role="button"
        tabIndex={0}>
        Hover me!
      </div>
      <ul
        style={{
          maxHeight: '200px',
          overflow: 'auto'
        }}>
        {events.map((e, i) => (
          <li key={i}>{e}</li>
        ))}
      </ul>
    </>
  );
}
function Example() {
  let [
    events,
    setEvents
  ] = React.useState([]);
  let {
    hoverProps,
    isHovered
  } = useHover({
    onHoverStart: (e) =>
      setEvents(
        (events) => [
          ...events,
          `hover start with ${e.pointerType}`
        ]
      ),
    onHoverEnd: (e) =>
      setEvents(
        (events) => [
          ...events,
          `hover end with ${e.pointerType}`
        ]
      )
  });

  return (
    <>
      <div
        {...hoverProps}
        style={{
          background: isHovered
            ? 'darkgreen'
            : 'green',
          color: 'white',
          display:
            'inline-block',
          padding: 4,
          cursor:
            'pointer'
        }}
        role="button"
        tabIndex={0}>
        Hover me!
      </div>
      <ul
        style={{
          maxHeight:
            '200px',
          overflow:
            'auto'
        }}>
        {events.map(
          (e, i) => (
            <li key={i}>
              {e}
            </li>
          )
        )}
      </ul>
    </>
  );
}