# useFluidHover

Headless proximity tracking for custom lists and grids.

> For the complete documentation index, see [llms.txt](/llms.txt). Markdown variants are available by appending `.md` to any URL or sending an `Accept: text/markdown` header. An agent skill is available at [/.well-known/agent-skills/site-skill.md](/.well-known/agent-skills/site-skill.md).





useFluidHover is the geometry and pointer-tracking part of [Fluid Hover](/docs/foundations/fluid-hover). It tells you which registered item is nearest and where that item's rectangle sits inside the container. It renders nothing.

A normal list can rely on each item's CSS hover state. A highlight that travels across the whole group needs a shared view of every item's position, including the spaces between them. Keeping that work in a hook avoids rebuilding measurement, pointer tracking, and cleanup for each menu, grid, or custom list.

## When to use it [#when-to-use-it]

Use this hook when you are building your own interactive group. Use the Foundation's existing demos and highlight when you only need the standard presentation. You do not need this hook just to color a single button on hover.

Register the actual target elements, attach the returned handlers to the positioned container, and render the highlight separately. The returned active index is transient hover state, not your selected value. Keep selection in your own state and leave keyboard navigation to semantic controls or Base UI.

The preview is deliberately made from ordinary buttons. Move between rows, click one, then Tab through the buttons: pointer feedback and actual activation remain separate.

## Preview [#preview]

<DocsPreview code="<ComponentSource src=&#x22;examples/foundations/fluid-hover.tsx&#x22; />">
  <FluidListDemo />
</DocsPreview>

## Installation [#installation]

The starter includes `hooks/use-fluid-hover.ts`. Import the hook directly; no application-wide provider is required.

## Usage [#usage]

```tsx
import { useFluidHover } from "@/hooks/use-fluid-hover";

const ref = useRef<HTMLDivElement>(null);
const hover = useFluidHover(ref, { axis: "y", gapClick: false });
// Spread hover.handlers on the container.
// Register each item with hover.registerItem(index, node).
// Render <FluidHoverHighlight hover={hover} /> separately.
```

## API Reference [#api-reference]

| API                            | Type                             | Behavior / default                                    |
| ------------------------------ | -------------------------------- | ----------------------------------------------------- |
| `containerRef`                 | `RefObject<HTMLElement \| null>` | `Positioned container`                                |
| `options.axis`                 | `x, y, xy`                       | `y`                                                   |
| `options.isItemDisabled`       | `(element) => boolean`           | `Disabled and hidden items are skipped automatically` |
| `options.gapClick`             | `boolean or { maxDistance }`     | `true`                                                |
| `activeIndex / setActiveIndex` | `number or null / setter`        | `Current nearest item; no focus movement`             |
| `itemRects / isMeasured`       | `Record of rects / boolean`      | `Measured in container coordinates`                   |
| `registerItem(index, node)`    | `Callback`                       | `Register or unregister an item`                      |
| `remeasure / measureItems`     | `Functions`                      | `Scheduled or immediate measurement`                  |
| `handlers`                     | `Pointer handlers and onClick`   | `Spread onto the container`                           |
| `sessionRef`                   | `RefObject<number>`              | `New pointer entry starts a fresh highlight`          |

## Behavior [#behavior]

The interactive example above is a custom list built from ordinary buttons, not a menu primitive. Use x for horizontal lists, y for menus and table rows, and xy for grids. Item containment wins; otherwise the nearest center wins. Touch input leaves native scrolling and activation alone. ResizeObserver and mutation observation keep the geometry current. Register stable indexes and use a positioned container. Keyboard focus remains the responsibility of your semantic controls or Base UI. See [Fluid Hover](/docs/foundations/fluid-hover) for all three layouts.

## Implementation [#implementation]

<ComponentSource src="hooks/use-fluid-hover.ts" />
