Skip to content

useMeasure

Measure element dimensions with ResizeObserver

Measures an element's dimensions using ResizeObserver. Returns a ref callback and the current bounds.

Installation

npm install betteruse

Usage

import { useMeasure } from 'betteruse'

function ResponsiveComponent() {
  const [ref, bounds] = useMeasure()

  return (
    <div ref={ref}>
      <p>Width: {bounds.width}px</p>
      <p>Height: {bounds.height}px</p>
    </div>
  )
}

API Reference

useMeasure()

Returns a tuple of [ref, bounds].

ref

A callback ref to attach to the element you want to measure.

Bounds

PropTypeDefaultDescription
widthnumber-Content width of the element
heightnumber-Content height of the element
topnumber-Top position relative to viewport
leftnumber-Left position relative to viewport
rightnumber-Right position relative to viewport
bottomnumber-Bottom position relative to viewport
xnumber-X coordinate (same as left)
ynumber-Y coordinate (same as top)

How It Works

  1. Creates a ResizeObserver when an element is attached
  2. Observes the element for size changes
  3. Updates bounds state on each resize
  4. Cleans up observer when element is removed or component unmounts

This hook is SSR-safe. ResizeObserver is only created on the client.

Examples

Responsive Layout

function AdaptiveLayout() {
  const [ref, { width }] = useMeasure()

  return (
    <div ref={ref}>
      {width > 768 ? (
        <DesktopLayout />
      ) : width > 480 ? (
        <TabletLayout />
      ) : (
        <MobileLayout />
      )}
    </div>
  )
}

Aspect Ratio Container

function VideoContainer() {
  const [ref, { width }] = useMeasure()
  const height = width * (9 / 16) // 16:9 aspect ratio

  return (
    <div ref={ref} style={{ height }}>
      <video style={{ width: '100%', height: '100%' }} />
    </div>
  )
}

Canvas Sizing

function ResponsiveCanvas() {
  const canvasRef = useRef<HTMLCanvasElement>(null)
  const [containerRef, { width, height }] = useMeasure()

  useEffect(() => {
    if (canvasRef.current) {
      const dpr = window.devicePixelRatio || 1
      canvasRef.current.width = width * dpr
      canvasRef.current.height = height * dpr

      const ctx = canvasRef.current.getContext('2d')
      ctx?.scale(dpr, dpr)
      // Draw...
    }
  }, [width, height])

  return (
    <div ref={containerRef} className="w-full h-full">
      <canvas
        ref={canvasRef}
        style={{ width, height }}
      />
    </div>
  )
}

Text Truncation

function AutoTruncate({ text }: { text: string }) {
  const [ref, { width }] = useMeasure()
  const charsPerPixel = 0.1 // Approximate
  const maxChars = Math.floor(width * charsPerPixel)

  return (
    <div ref={ref}>
      {text.length > maxChars
        ? `${text.slice(0, maxChars)}...`
        : text}
    </div>
  )
}

Chart Sizing

function ResponsiveChart({ data }: { data: number[] }) {
  const [ref, { width, height }] = useMeasure()

  return (
    <div ref={ref} className="w-full h-64">
      {width > 0 && height > 0 && (
        <svg width={width} height={height}>
          {/* Render chart with actual dimensions */}
        </svg>
      )}
    </div>
  )
}

Performance

The hook uses ResizeObserver which is optimized by the browser:

  • Updates are batched with other layout operations
  • Observations are passive and don't block the main thread
  • Memory is properly cleaned up when elements are removed

TypeScript

The hook accepts a generic type parameter for the element type:

const [ref, bounds] = useMeasure<HTMLDivElement>()

<div ref={ref}>...</div>

This provides proper typing for the ref callback.