useElementHeight
ericts-ui/use-element-heightFreeHook
A client-safe React hook that measures an element's height with ResizeObserver so a container can animate between content heights.
Install it
npx shadcn@latest add https://ui.ericts.com/r/use-element-height.jsonSource
1"use client";23import { useCallback, useRef, useState } from "react";45/**6 * Tracks an element's rendered height so a container can animate between content7 * heights — accordions, collapsibles, step flows, expanding cards, anything that8 * grows or shrinks to fit its content.9 *10 * Why this exists: you still can't reliably CSS-transition `height: auto` across11 * browsers (the native `interpolate-size` / `calc-size()` approach is promising12 * but not yet universally supported). The portable pattern is "measure, then13 * animate to a pixel value" — which is exactly what this hook gives you.14 *15 * @example16 * const [ref, height] = useElementHeight<HTMLDivElement>();17 * return (18 * <motion.div animate={{ height: height ?? "auto" }} className="overflow-hidden">19 * <div ref={ref}>{content}</div>20 * </motion.div>21 * );22 *23 * Notes for animators:24 * - Uses a *callback ref*, so it re-measures correctly even when the measured25 * element is conditionally rendered or swapped out — the common case for26 * collapsibles. (A `useRef` + `useEffect([])` hook would silently miss that.)27 * - Reads the *border-box* height (padding + border included), because that's the28 * box the parent actually has to grow to.29 * - `threshold` ignores sub-pixel ResizeObserver noise that would otherwise30 * re-trigger the animation every frame and read as jitter.31 */32export function useElementHeight<T extends HTMLElement>(threshold = 0.5) {33 const [height, setHeight] = useState<number | null>(null);34 const observerRef = useRef<ResizeObserver | null>(null);3536 const ref = useCallback(37 (element: T | null) => {38 // Tear down the previous observer whenever the element changes or unmounts.39 observerRef.current?.disconnect();4041 if (!element) return;4243 const updateHeight = (nextHeight: number) => {44 setHeight((currentHeight) => {45 if (currentHeight === null) return nextHeight;4647 return Math.abs(currentHeight - nextHeight) > threshold48 ? nextHeight49 : currentHeight;50 });51 };5253 updateHeight(element.getBoundingClientRect().height);5455 if (typeof ResizeObserver === "undefined") return;5657 const observer = new ResizeObserver((entries) => {58 const entry = entries[0];5960 if (!entry) return;6162 const borderBoxSize = Array.isArray(entry.borderBoxSize)63 ? entry.borderBoxSize[0]64 : entry.borderBoxSize;6566 updateHeight(67// … truncated
Files it writes
More from ericts-ui
All 38 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| useElementSizeMapuse-element-size-map | ericts-ui | Hooks | Free | no deps | |
| useReducedMotionuse-reduced-motion | ericts-ui | Hooks | Free | no deps | |
| useScrollAnchoruse-scroll-anchor | ericts-ui | Hooks | Free | no deps | |
| useScrollProgressuse-scroll-progress | ericts-ui | Hooks | Free | no deps |
