# useResizeObserver > Measure an element's size reactively via ResizeObserver. - Category: DOM - Package: `hookli` (install with `npm i hookli`) - Docs: https://hookli.vercel.app/docs/use-resize-observer ## Signature ```ts useResizeObserver(ref: RefObject, options?: UseResizeObserverOptions): ResizeObserverSize ``` ## Parameters - `ref`: `RefObject` — Ref to the element to measure. The observer attaches to ref.current. - `options`: `UseResizeObserverOptions` (default: `{}`) — Which box to measure and an optional resize callback. ## Returns - `width`: `number | undefined` — The element's measured width; undefined until the first observed layout. - `height`: `number | undefined` — The element's measured height; undefined until the first observed layout. ## Types ### UseResizeObserverOptions Configures the underlying ResizeObserver. - `box`: `ResizeObserverBoxOptions` (default: `"content-box"`) — Which box model to measure: content-box, border-box or device-pixel-content-box. - `onResize`: `(size: ResizeObserverSize) => void` — Called with the freshly measured size on every resize. ### ResizeObserverSize The size the hook returns; both values are undefined until the first measurement. - `width`: `number | undefined` — Measured width in pixels. - `height`: `number | undefined` — Measured height in pixels. ## Usage ```tsx import { useRef } from "react"; import { useResizeObserver } from "hookli"; export function Demo() { const boxRef = useRef(null); const { width, height } = useResizeObserver(boxRef); return (
{width === undefined ? "Measuring…" : `${Math.round(width)} × ${Math.round(height ?? 0)}`}
); } ``` ## Source `src/hooks/use-resize-observer/use-resize-observer.ts` ```ts import { RefObject, useEffect, useRef, useState } from "react"; interface ResizeObserverSize { width: number | undefined; height: number | undefined; } interface UseResizeObserverOptions { box?: ResizeObserverBoxOptions; onResize?: (size: ResizeObserverSize) => void; } export const useResizeObserver = ( ref: RefObject, options: UseResizeObserverOptions = {}, ): ResizeObserverSize => { const { box = "content-box" } = options; const [size, setSize] = useState({ width: undefined, height: undefined, }); const onResizeRef = useRef(options.onResize); onResizeRef.current = options.onResize; const previous = useRef({ width: undefined, height: undefined, }); useEffect(() => { const element = ref.current; if (!element) return; if (typeof ResizeObserver === "undefined") return; const observer = new ResizeObserver(([entry]) => { if (!entry) return; const boxSize = box === "border-box" ? entry.borderBoxSize : box === "device-pixel-content-box" ? entry.devicePixelContentBoxSize : entry.contentBoxSize; const measured = Array.isArray(boxSize) ? boxSize[0] : boxSize; const width = measured ? measured.inlineSize : entry.contentRect.width; const height = measured ? measured.blockSize : entry.contentRect.height; if ( previous.current.width === width && previous.current.height === height ) { return; } const next = { width, height }; previous.current = next; setSize(next); onResizeRef.current?.(next); }); observer.observe(element, { box }); return () => { observer.disconnect(); }; }, [ref, box]); return size; }; ```