Smooth Height Layout
ericts-ui/smooth-heightFreeComponent
A Motion-powered layout primitive that observes content size and animates height changes.
Live preview
live · rendered by the registry
Rendered by ericts-ui on their own page — this is the component running, not a screenshot of it.Install it
npx shadcn@latest add https://ui.ericts.com/r/smooth-height.jsonSource
1"use client";23import * as React from "react";4import {5 motion,6 useReducedMotion,7 type HTMLMotionProps,8} from "motion/react";910import { cn } from "@/lib/utils";1112type MotionTransition = NonNullable<HTMLMotionProps<"div">["transition"]>;1314export type SmoothHeightProps = Omit<15 HTMLMotionProps<"div">,16 "animate" | "children" | "initial" | "transition"17> & {18 children: React.ReactNode;19 innerClassName?: string;20 transition?: MotionTransition;21};2223export function SmoothHeight({24 children,25 className,26 innerClassName,27 transition,28 ...props29}: SmoothHeightProps) {30 const [height, setHeight] = React.useState<number | null>(null);31 const contentRef = React.useRef<HTMLDivElement>(null);32 const shouldReduceMotion = useReducedMotion();3334 React.useEffect(() => {35 const content = contentRef.current;3637 if (!content) return;3839 const updateHeight = (nextHeight: number) => {40 setHeight((currentHeight) => {41 if (currentHeight === null) return nextHeight;4243 return Math.abs(currentHeight - nextHeight) > 0.544 ? nextHeight45 : currentHeight;46 });47 };4849 updateHeight(content.getBoundingClientRect().height);5051 if (typeof ResizeObserver === "undefined") return;5253 const observer = new ResizeObserver((entries) => {54 const entry = entries[0];5556 if (!entry) return;5758 const borderBoxSize = Array.isArray(entry.borderBoxSize)59 ? entry.borderBoxSize[0]60 : entry.borderBoxSize;6162 updateHeight(63 borderBoxSize?.blockSize ?? entry.target.getBoundingClientRect().height64 );65 });6667 observer.observe(content);6869 return () => observer.disconnect();70 }, []);7172 const resolvedTransition: MotionTransition = shouldReduceMotion73 ? { duration: 0 }74 : transition ?? { duration: 0.3, ease: [0.645, 0.045, 0.355, 1] };7576 return (77 <motion.div78 {...props}79 data-slot="smooth-height"80 initial={false}81 animate={{ height: height ?? "auto" }}82 transition={resolvedTransition}83 className={cn("overflow-hidden", className)}84 >85 <div86 ref={contentRef}87 data-slot="smooth-height-content"88 className={innerClassName}89 >90 {children}91 </div>92 </motion.div>93 );94}
What it pulls in
npm packages
Files it writes
More from ericts-ui
All 38 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Adaptive Draweradaptive-drawer | ericts-ui | Components | Free | 2 deps | |
| Check Animationcheck-animation | ericts-ui | Components | Free | no deps · 2 files | |
| Context Cursorcontext-cursor | ericts-ui | Components | Free | 1 dep | |
| Copy Buttoncopy-button | ericts-ui | Components | Free | 2 deps | |
| Expandable Dialogexpandable-modal | ericts-ui | Components | Free | 1 dep | |
| Expandable Tabsexpandable-tabs | ericts-ui | Components | Free | 1 dep | |
| Expandable Toolbarexpandable-toolbar | ericts-ui | Components | Free | 1 dep | |
| Expanding Panelexpanding-panel | ericts-ui | Components | Free | 2 deps |
