shadcn.io is an independent project built on shadcn/ui
useResizeObserver watches one element and returns its width and height. Those numbers update when the element box changes, whether the cause is layout, content, or a drag.
The default measurement is the content box, which leaves padding and border out. For a viewport query instead of one element, use useMediaQuery. In React both numbers stay unset until the observer reports a size.
Installation
Anatomy

Usage
import { useRef } from "react";
import { useResizeObserver } from "@/hooks/use-resize-observer";const panelRef = useRef<HTMLDivElement>(null);
const { width, height } = useResizeObserver({ ref: panelRef });width and height are undefined until the first observation. Pass box to measure the border box or device pixels. If you pass onResize, the hook calls it and leaves width and height undefined.
FAQ
Which box is measured?
The default is content-box, so the number is the content area and padding and border are not included. border-box includes padding and border. device-pixel-content-box reports physical device pixels, which a canvas can use. If a browser omits content-box sizes, the hook falls back to contentRect. The other two boxes do not fall back. They stay undefined until the entry includes them.
When should I pass onResize?
Pass onResize when you want the measurement without React state. While that callback is set, the hook calls it and does not update the width and height it returns, so those stay undefined. Leave it off when the render should read the numbers. The callback is held in a ref, so a new function does not rebuild the observer. Use that for a canvas or any side effect.
What are width and height before the first report?
Both start undefined. State is created that way, and the observer is set up in an effect, so the server render and the first client paint have no numbers. Browsers usually report the current size soon after observe. Treat undefined as not measured yet, not as zero. A later report with the same width and height does not set state again.
Does the ref have to be set before the effect?
The effect reads ref.current when it runs. It does not subscribe later just because a node appears. On a normal commit the ref is already set, so the element is observed. If current is null at that moment, nothing is watched until the ref object, the box, or the mount flag changes. Pass the stable object from useRef, not a fresh object each render.
Is one observer per element expensive?
The browser batches these notifications, and the hook creates one observer for the single element on the ref. Cleanup disconnects it. A report whose width and height match the previous one does not call setState. The hook does not poll and it does not listen for window resize. To watch several elements, call the hook once for each, with its own ref.
Is it safe during server rendering?
Yes. Width and height start undefined, and the observer is created in an effect that returns immediately when window or ResizeObserver is missing. Render never touches the DOM. After mount, the observer uses the content box unless you passed another box. Unmount disconnects it. A mounted flag also blocks a late notification from calling setState after cleanup.
API Reference
Parameters
optionsUseResizeObserverOptions<HTMLElement>
The element's ref, the box to measure, and an optional callback.
- Type
UseResizeObserverOptions<HTMLElement>
options.box?"border-box" | "content-box" | "device-pixel-content-box"
Which box is measured: the content (inside padding), the border box, or the content in device pixels.
- Type
"border-box" | "content-box" | "device-pixel-content-box"- Default
"content-box"
options.onResize?(size: Size) => void
Called with each new size instead of updating the returned one, so the component doesn't re-render (the returned size stays undefined).
- Type
(size: Size) => void
options.refReact.RefObject<HTMLElement | null>
A ref to the element to measure.
- Type
React.RefObject<HTMLElement | null>
Returns
heightnumber | undefined
The element's height in pixels, undefined until it's measured.
- Type
number | undefined
widthnumber | undefined
The element's width in pixels, undefined until it's measured.
- Type
number | undefined











