DOM
useResizeObserver
Measure an element's size reactively via ResizeObserver.
Demo
live · imported from hookli
drag my corner ↘
- width
- —
- height
- —
Reports element size without a window resize — the values track both the preset buttons and the native drag handle.
import { useRef } from "react";
import { useResizeObserver } from "hookli";
export function Demo() {
const boxRef = useRef<HTMLDivElement>(null);
const { width, height } = useResizeObserver(boxRef);
return (
<div ref={boxRef}>
{width === undefined ? "Measuring…" : `${Math.round(width)} × ${Math.round(height ?? 0)}`}
</div>
);
}Usage
import { useRef } from "react";
import { useResizeObserver } from "hookli";
export function Demo() {
const boxRef = useRef<HTMLDivElement>(null);
const { width, height } = useResizeObserver(boxRef);
return (
<div ref={boxRef}>
{width === undefined ? "Measuring…" : `${Math.round(width)} × ${Math.round(height ?? 0)}`}
</div>
);
}API
useResizeObserver<T extends HTMLElement = HTMLElement>(ref: RefObject<T>, options?: UseResizeObserverOptions): ResizeObserverSizeParameters
| Name | Type | Default | Description |
|---|---|---|---|
| ref | RefObject<T> | — | Ref to the element to measure. The observer attaches to ref.current. |
| options | UseResizeObserverOptions | {} | Which box to measure and an optional resize callback. |
Returns
| Name | Type | Description |
|---|---|---|
| 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. |
UseResizeObserverOptions
Configures the underlying ResizeObserver.
| Name | Type | Description |
|---|---|---|
| box | ResizeObserverBoxOptions | 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.
| Name | Type | Description |
|---|---|---|
| width | number | undefined | Measured width in pixels. |
| height | number | undefined | Measured height in pixels. |
Hook
The implementation, straight from hookli. Copy it, or install the package.
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 = <T extends HTMLElement = HTMLElement>(
ref: RefObject<T>,
options: UseResizeObserverOptions = {},
): ResizeObserverSize => {
const { box = "content-box" } = options;
const [size, setSize] = useState<ResizeObserverSize>({
width: undefined,
height: undefined,
});
const onResizeRef = useRef(options.onResize);
onResizeRef.current = options.onResize;
const previous = useRef<ResizeObserverSize>({
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;
};