Aspect ratio
A box that holds its shape before media loads, shimmers while loading, fades the media in and falls back when it fails.
pnpm dlx shadcn@latest add https://hextaui.com/r/aspect-ratio.jsonAdds the component, the HextaUI theme tokens and any HextaUI components it depends on.
Add the theme tokens to your global CSS, if you haven’t yet.
Install the dependencies.
pnpm add @base-ui/react @tabler/icons-react class-variance-authority cnCopy and paste the following code into your project.
components/ui/aspect-ratio.tsx components/ui/skeleton.tsx Update the import paths to match your project setup.
ratio takes a number, a "w/h" string or a "w:h" string.
The box holds its shape and shimmers until the image arrives, then the image fades in, so nothing below it moves. Press Reload to watch it again.
When the image fails, the browser’s broken-image glyph is hidden and a fallback icon is shown instead. Pass fallback to replace it, or fallback={null} to show nothing.
Absolutely positioned children sit on top of the media. The box clips nothing, so focus rings on overlay links stay visible.
placeholder={false} turns off the loading shimmer, the fade-in and the fallback, for the plain shadcn behavior.
Override the ratio at a breakpoint with an aspect class. This one is square on small screens and md:aspect-video from md up.
The box is full width by default, so it fills the column instead of collapsing to zero when the parent centers its children.
Children that aren’t media get the box and nothing else. Position them yourself.
Keep captions outside the box so they don’t change the ratio.
0, negative numbers and unparsable strings fall back to a square and log a warning in development.
Overlays positioned with logical properties like start-3 follow the reading direction.
- The box is
aria-busywhile its media loads. - The fallback is decorative and hidden from assistive tech. The image’s
alttext stays available when it fails to load, so always write one. - With reduced motion on, media appears without fading.
Accepts every attribute of the element it renders. Media placed directly inside, an <img>, <picture> or <video>, fills the box with object-cover and inherits its radius.
| Prop | Type | Default |
|---|---|---|
ratio | number | `${number}/${number}` | `${number}:${number}` | 1 |
placeholderShows a shimmer while the media loads and a fallback when it fails. | boolean | true |
fallbackShown when the media fails. null shows nothing. | ReactNode | <IconPhotoOff /> |
render | ReactElement | (props, state) => ReactElement | <div> |
| Attribute | Description |
|---|---|
data-slot="aspect-ratio" | Target the box in CSS. |
data-state | loading, loaded or error. Set only when placeholder is on and the box holds media. |
aria-busy | Present while the media loads. |
--ratio | The parsed ratio as a number. |
data-slot="aspect-ratio-placeholder" | The shimmer shown while loading or after an error. |
data-slot="aspect-ratio-fallback" | The wrapper around the fallback. |
parseAspectRatio(ratio) turns any accepted ratio into a number, falling back to 1. Use it to size other elements the same way. The AspectRatioValue and AspectRatioProps types are exported too.
- SkeletonPlaceholders that wait 150ms before showing, take the exact size of the content they wrap, and fade it in without moving anything.
- AccordionStacked headings that each reveal a panel, with height motion you can reverse mid-way and panels that stay searchable while closed.
- CarouselNative scroll-snap slides with momentum on touch, mouse dragging, arrow keys, dots, thumbnails and an autoplay that pauses when it should.
- CollapsibleA panel that shows and hides with height motion you can reverse mid-way, without jumping the layout.
- ResizablePanels you can drag apart, with a quiet divider that wakes up on hover, sizes that glide on reset or collapse, and layouts that persist.
- Scroll areaNative scrolling with a minimal scrollbar, edges that fade only where there’s more to see, and an optional peek that cuts the last item in half.