For the complete documentation index, see 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.
1
Sponsor

useMeasure

Observe an element's border-box dimensions as its content changes.

useMeasure observes an element's layout width and height. It is useful when another part of the interface needs a numeric size, not just CSS layout.

An expanding container illustrates the problem: its content may change because text wraps, a section opens, or its available width changes. A one-time measurement becomes stale. ResizeObserver lets the hook respond to the element itself instead of guessing from window resize events.

Measure only what JavaScript needs

Prefer CSS for ordinary responsive layout. Use measurement when you need to animate a numeric height or coordinate another element with the measured content. Measure the inner content and animate the outer wrapper; measuring the element whose size you are setting can create a feedback loop.

The result is the border-box layout size, not the apparent size after a transform. It starts at zero before the element is observed, so avoid treating that initial value as a real collapsed state during server rendering.

Expand the content below. The inner measurement changes, and the wrapper follows it using the shared Motion preset.

Preview

This container follows its measured content.

0 × 0px

Installation

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

Usage

import { useMeasure } from "@/hooks/use-measure";
 
const { ref, width, height } = useMeasure<HTMLDivElement>();
return (
  <div ref={ref}>
    {width} × {height}
  </div>
);

API Reference

APITypeBehavior / default
refCallback refAttach to the element to observe
widthnumberBorder-box width in layout pixels
heightnumberBorder-box height in layout pixels

Behavior

The demo measures inner content while the outer container animates its height using the shared layout spring. Width and height start at zero on the server. The observer attaches after hydration, follows ref replacement, and disconnects on unmount. Transform scale is deliberately excluded: these are layout dimensions.

Implementation

"use client";

import { useCallback, useEffect, useState } from "react";

export const useMeasure = <T extends HTMLElement = HTMLDivElement>() => {
  const [element, setElement] = useState<T | null>(null);
  const [size, setSize] = useState({ height: 0, width: 0 });
  const ref = useCallback((node: T | null) => {
    setElement(node);
  }, []);
  useEffect(() => {
    if (!element) {
      return;
    }
    const update = () => {
      const width = element.offsetWidth;
      const height = element.offsetHeight;
      setSize((previous) =>
        previous.width === width && previous.height === height
          ? previous
          : { height, width }
      );
    };
    update();
    const observer = new ResizeObserver(update);
    observer.observe(element);
    return () => observer.disconnect();
  }, [element]);
  return { ref, ...size };
};