Hooks
useSize
Observe and return a DOM element’s width and height using ResizeObserver — updates synchronously via a layout effect and returns undefined until an element is available.
useSize
useSize measures a DOM element’s width and height using ResizeObserver, returning a { width, height } object that updates whenever the element’s border-box size changes. It uses a layout effect so the first size is provided as early as possible on the client.
- Returns
undefineduntil an element is available - Updates on any size change (content, border, padding)
- Uses border-box sizing for consistent measurements
Pass a stable element reference (e.g. ref.current or a callback ref state) to useSize. When the element changes, the observer is reattached.
API
function useSize(
element: HTMLElement | null
): { width: number; height: number } | undefined;Prop
Type
Usage
1) Basic measurement with a standard ref
import * as React from 'react';
import { useSize } from '@loke/ui/use-size';
export function MeasuredPanel() {
const ref = React.useRef<HTMLDivElement | null>(null);
const size = useSize(ref.current); // width/height or undefined
return (
<div className="space-y-3">
<div ref={ref} className="rounded border bg-card p-6">
Resize your window or change content to see updates.
</div>
<div className="text-xs text-muted-foreground">
{size ? `w: ${size.width}px, h: ${size.height}px` : 'Measuring…'}
</div>
</div>
);
}2) Using a callback ref (stable across re-renders)
import * as React from 'react';
import { useSize } from '@loke/ui/use-size';
export function CallbackRefExample() {
const [el, setEl] = React.useState<HTMLDivElement | null>(null);
const size = useSize(el);
return (
<div>
<div ref={setEl} className="rounded border bg-card p-4">
Observed via a callback ref.
</div>
<pre className="mt-2 text-xs text-muted-foreground">
{JSON.stringify(size)}
</pre>
</div>
);
}3) Drive layout from size (e.g., responsive columns)
import * as React from 'react';
import { useSize } from '@loke/ui/use-size';
export function CardGrid() {
const ref = React.useRef<HTMLDivElement | null>(null);
const size = useSize(ref.current);
const colWidth = 280;
const cols = Math.max(1, Math.floor((size?.width ?? colWidth) / colWidth));
return (
<div ref={ref}>
<div
className="grid gap-3"
style={{ gridTemplateColumns: `repeat(${cols}, minmax(0, 1fr))` }}
>
{Array.from({ length: 8 }).map((_, i) => (
<div key={i} className="rounded border bg-card p-4">
Card {i + 1}
</div>
))}
</div>
</div>
);
}Behavior & details
- First measurement:
- On mount (client), the hook sets an initial
{ width, height }usingoffsetWidth/offsetHeightto provide values as early as possible before observer events.
- On mount (client), the hook sets an initial
- Subsequent updates:
- Uses
ResizeObserverwith the border-box to keep values accurate across padding and border changes.
- Uses
- Cleanup:
- Automatically disconnects the observer when the element changes or unmounts.
The hook returns undefined when no element is available. Always guard against this when rendering measurements.
SSR and environments
- This hook uses a layout effect and
ResizeObserver, which are browser-only features. - Use it in Client Components (e.g., Next.js
"use client") or guard usage in non-DOM environments. - During SSR, skip rendering UI that depends on the measurement until hydrated, or render a fallback.
Tips
- Prefer a callback ref (e.g., setEl) if the element itself may change across renders.
- If you only care about content-box changes, you can adapt the hook (this version returns border-box sizes).
- Combine with your responsive primitives to simplify layout decisions (e.g., switch between Columns configs based on width).
Import
import { useSize } from '@loke/ui/use-size';