A box that keeps a fixed width to height ratio for what it holds, such as an image, a video or a map. It reserves the space before the content loads, so the page does not jump.
import { AspectRatio } from "@rdloom/react";
export default function AspectRatioVideoExample() {
return (
<div className="flex w-full justify-center">
<div className="w-[28rem] max-w-full">
<AspectRatio ratio="video" rounded>
<div className="flex size-full items-center justify-center bg-gradient-to-br from-[var(--rd-color-surface-subtle)] to-[var(--rd-color-border-default)] text-sm text-[var(--rd-color-text-muted)]">
16:9 video area
</div>
</AspectRatio>
</div>
</div>
);
}Installation
npx rdloom add aspect-ratioCopies the source into src/components/rdloom. Edit it freely: rdloom upgrade merges later versions into your changes.
Use another registry client
npx shadcn@latest add https://rdloom.vimalbhatt.com/r/aspect-ratio.jsonWorks, but without upgrade tracking.
Usage
import { AspectRatio } from "@rdloom/react";
<div className="flex w-full justify-center">
<div className="w-[28rem] max-w-full">
<AspectRatio ratio="video" rounded>
<div className="flex size-full items-center justify-center bg-gradient-to-br from-[var(--rd-color-surface-subtle)] to-[var(--rd-color-border-default)] text-sm text-[var(--rd-color-text-muted)]">
16:9 video area
</div>
</AspectRatio>
</div>
</div>Square
import { AspectRatio } from "@rdloom/react";
export default function AspectRatioSquareExample() {
return (
<div className="flex w-full justify-center">
<div className="w-48">
<AspectRatio ratio="square" rounded>
<div
role="img"
aria-label="Image placeholder"
className="size-full bg-gradient-to-tr from-[var(--rd-color-action-primary)] to-[var(--rd-color-surface-subtle)]"
/>
</AspectRatio>
</div>
</div>
);
}Portrait
import { AspectRatio } from "@rdloom/react";
export default function AspectRatioPortraitExample() {
return (
<div className="flex w-full justify-center">
<div className="w-40">
<AspectRatio ratio="portrait" rounded>
<div className="flex size-full items-end bg-gradient-to-b from-[var(--rd-color-surface-subtle)] to-[var(--rd-color-border-strong)] p-3 text-sm text-[var(--rd-color-text-default)]">
3:4 cover
</div>
</AspectRatio>
</div>
</div>
);
}API Reference
Defined by the spec. Components also accept the props of the React Aria component they wrap.
| Prop | Type | Default |
|---|---|---|
ratioWidth divided by height as a number (1.5), a "16/9" string, or a preset: square 1/1, video 16/9, photo 3/2, wide 21/9, portrait 3/4. | number | "square" | "video" | "photo" | "wide" | "portrait" | `${number}/${number}` | "video" |
roundedRounds the corners with the control radius. | boolean | false |
childrenThe content. Images, video and iframes are stretched to fill the box and cropped to cover it. | node | none |
Accessibility
Role none, WCAG 2.2 AA. Tested with axe and keyboard tests; screen reader checks are in the audit checklist.
Keyboard
- Not interactive: it adds no tab stops
Screen readers announce
- Transparent to screen readers; only the content inside is announced
What your code must do
- The box adds no role or name; the content inside carries its own, such as alt text on an image
- Content that is cropped must not lose information that matters: pick a ratio that fits it
Guidelines
Use it when
- Media whose size is not known before it loads
- A grid of tiles that must line up
- A video or map embed that should scale with its container
Avoid it when
- Text content: it can overflow the fixed box
- Content that must never be cropped, such as a chart with labels at the edges
Don't
- Putting long text inside, which gets clipped
- Setting a height on the box, which fights the ratio
Design tokens
The semantic tokens this component uses. Change them once and every component follows; see Design tokens.
--rd-radius-control