Bloom Logo

Aspect Ratio

Displays content within a desired aspect ratio, preserving proportions responsively across viewport sizes with ratio presets and skeleton loading placeholders.

importimport { AspectRatio } from "@/components/ui/aspectRatio/aspectRatio";
$npx @bloomui-react/cli add aspectRatio
aspectRatio.tsx
"use client";

import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio";
import * as React from "react";
import { cn } from "@/lib/utils";

export type AspectRatioPreset =
  | "video"
  | "square"
  | "golden"
  | "cinema"
  | "portrait"
  | "ultrawide";

const ratioPresets: Record<AspectRatioPreset, number> = {
  video: 16 / 9,
  square: 1 / 1,
  golden: 1.618 / 1,
  cinema: 21 / 9,
  portrait: 4 / 5,
  ultrawide: 32 / 9,
};

export interface AspectRatioProps
  extends React.ComponentPropsWithoutRef<typeof AspectRatioPrimitive.Root> {
  ratio?: number;
  preset?: AspectRatioPreset;
  isLoading?: boolean;
}

const AspectRatio = React.forwardRef<
  React.ElementRef<typeof AspectRatioPrimitive.Root>,
  AspectRatioProps
>(
  (
    { ratio, preset, isLoading = false, className, children, ...props },
    ref,
  ) => {
    const computedRatio = preset ? ratioPresets[preset] : (ratio ?? 16 / 9);

    return (
      <div
        className={cn("relative w-full overflow-hidden rounded-2xl", className)}
      >
        <AspectRatioPrimitive.Root ref={ref} ratio={computedRatio} {...props}>
          {isLoading && (
            <div className="absolute inset-0 z-10 bg-zinc-200 dark:bg-zinc-800 animate-pulse flex items-center justify-center">
              <div className="size-8 rounded-full border-2 border-zinc-400 border-t-transparent animate-spin" />
            </div>
          )}
          {children}
        </AspectRatioPrimitive.Root>
      </div>
    );
  },
);

AspectRatio.displayName = AspectRatioPrimitive.Root.displayName;

export { AspectRatio };

Preset Ratios

Use predefined ratio aliases (video, square, golden, cinema, portrait, ultrawide).

preset: video | square | golden | cinema | portrait | ultrawide
preset="video" (16:9)
Video Widescreen
preset="square" (1:1)
Square Portrait
preset="cinema" (21:9)
Cinema Banner

Loading Skeleton Placeholder

Display a pulsing skeleton container while high-resolution media is loading.

isLoading: boolean
API Reference

Props — AspectRatio

Properties for configuring the AspectRatio component.

PropTypeDefaultDescription
preset'video' | 'square' | 'golden' | 'cinema' | 'portrait' | 'ultrawide'Preset aspect ratio alias. Takes precedence over custom numeric ratio.
rationumber16 / 9Desired custom width-to-height numeric ratio (e.g. 16/9, 4/3, 1/1).
isLoadingbooleanfalseShows an integrated skeleton loading spinner state inside the container frame.