Join our Discord Community

Aspect Ratio

Displays content within a desired ratio using CSS aspect-ratio or a JavaScript fallback.

Loading component...

Installation

npx shadcn@latest add aspect-ratio

Usage

import Image from "next/image"
import { AspectRatio } from "@/components/ui/aspect-ratio"

export function AspectRatioDemo() {
  return (
    <AspectRatio ratio={16 / 9} className="bg-muted">
      <Image
        src="/placeholder.jpg"
        alt="Image"
        fill
        className="rounded-md object-cover"
      />
    </AspectRatio>
  )
}

Examples

Square (1:1)

Loading component...

Portrait (3:4)

Loading component...

Multiple Ratios

Loading component...

API Reference

AspectRatio

Maintains a consistent width-to-height ratio for content.

PropTypeDefaultDescription
rationumber1The desired aspect ratio (width/height).
classNamestring-Additional CSS classes for styling.
childrenReact.ReactNode-The content to display within the aspect ratio container.

Implementation Details

Architecture

Built on Radix UI's AspectRatio primitive:

  • CSS First: Uses modern CSS aspect-ratio property when supported
  • JavaScript Fallback: Automatically falls back to JavaScript implementation for older browsers
  • Responsive: Maintains ratio across all screen sizes
  • Flexible: Works with any content type (images, videos, divs, etc.)

How It Works

The AspectRatio component calculates the appropriate height based on the container width and desired ratio:

// Example: 16:9 ratio
<AspectRatio ratio={16 / 9}>
  {/* Content automatically sized to maintain 16:9 ratio */}
</AspectRatio>

Common Ratio Values

RatioExpressionUse Case
Square1 / 1Profile pictures, thumbnails
Portrait3 / 4Vertical photos, mobile screens
Landscape4 / 3Traditional photos, presentations
Widescreen16 / 9Modern videos, hero images
Cinematic21 / 9Ultra-wide banners, movie clips
Golden Ratio1.618 / 1Aesthetically pleasing layouts

Best Practices

Image Implementation

Always use fill and object-cover with Next.js Image components:

<AspectRatio ratio={16 / 9} className="bg-muted overflow-hidden rounded-lg">
  <Image
    src="/image.jpg"
    alt="Description"
    fill
    className="object-cover"
  />
</AspectRatio>

Responsive Considerations

The component maintains its ratio at all screen sizes. For responsive ratios, use CSS classes:

<AspectRatio 
  ratio={16 / 9}
  className="md:aspect-[21/9] lg:aspect-[32/9]"
>
  <div>Responsive content</div>
</AspectRatio>

Performance Tips

  1. Image Optimization: Always use optimized images with appropriate dimensions
  2. Lazy Loading: Combine with Next.js Image lazy loading for better performance
  3. Container Sizing: Set explicit max-width on containers for predictable layouts

Common Use Cases

Video Embeds

Perfect for maintaining video aspect ratios:

<AspectRatio ratio={16 / 9}>
  <iframe
    src="https://www.youtube.com/embed/dQw4w9WgXcQ"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
    allowFullScreen
    className="w-full h-full rounded-lg"
  />
</AspectRatio>

Card Thumbnails

Consistent thumbnail sizes in grid layouts:

<div className="grid grid-cols-3 gap-4">
  {images.map((image) => (
    <AspectRatio key={image.id} ratio={1} className="bg-muted rounded-lg">
      <Image src={image.src} alt={image.alt} fill className="object-cover" />
    </AspectRatio>
  ))}
</div>

Hero Sections

Wide banner content with consistent proportions:

<AspectRatio ratio={21 / 9} className="bg-gradient-to-r from-blue-600 to-purple-600">
  <div className="flex items-center justify-center h-full text-white">
    <div className="text-center">
      <h1 className="text-4xl font-bold">Hero Content</h1>
      <p className="text-lg opacity-90">Perfectly proportioned banner</p>
    </div>
  </div>
</AspectRatio>

Dashboard Widgets

Consistent widget sizing in dashboard layouts:

<AspectRatio ratio={4 / 3} className="bg-card border rounded-lg p-4">
  <div className="h-full flex flex-col">
    <h3 className="font-semibold mb-2">Widget Title</h3>
    <div className="flex-1 flex items-center justify-center text-muted-foreground">
      Chart or content goes here
    </div>
  </div>
</AspectRatio>

Accessibility

Image Alt Text

Always provide meaningful alt text for images:

<AspectRatio ratio={16 / 9}>
  <Image
    src="/product.jpg"
    alt="Blue wireless headphones on a wooden desk with soft lighting"
    fill
    className="object-cover"
  />
</AspectRatio>

Interactive Content

Ensure interactive elements remain accessible within the aspect ratio container:

<AspectRatio ratio={16 / 9} className="bg-muted rounded-lg">
  <button 
    className="w-full h-full flex items-center justify-center hover:bg-accent transition-colors"
    aria-label="Play video"
  >
    <PlayIcon className="h-12 w-12 text-primary" />
  </button>
</AspectRatio>

Browser Support

  • Modern browsers: Uses CSS aspect-ratio property
  • Legacy browsers: Automatic JavaScript fallback via Radix UI
  • Mobile: Full support across all mobile browsers
  • Progressive Enhancement: Gracefully degrades in unsupported environments

Troubleshooting

Common Issues

  1. Images not displaying: Ensure fill prop is used with Next.js Image
  2. Overflow issues: Add overflow-hidden to the AspectRatio className
  3. Incorrect ratios: Double-check ratio calculation (width / height)
  4. Layout shifts: Set explicit container widths to prevent CLS

Debugging Tips

// Add visual debugging
<AspectRatio 
  ratio={16 / 9} 
  className="bg-red-100 border-2 border-red-500"
>
  <div className="bg-blue-100 h-full w-full flex items-center justify-center">
    Debug: {16/9} ratio
  </div>
</AspectRatio>

Integration Examples

With React Hook Form

function ImageUploadField() {
  const { register, watch } = useForm()
  const watchedImage = watch("image")

  return (
    <AspectRatio ratio={16 / 9} className="bg-muted border-2 border-dashed rounded-lg">
      {watchedImage ? (
        <Image src={watchedImage} alt="Upload preview" fill className="object-cover" />
      ) : (
        <div className="flex items-center justify-center h-full text-muted-foreground">
          <UploadIcon className="h-8 w-8 mb-2" />
          <p>Upload an image</p>
        </div>
      )}
      <input {...register("image")} type="file" className="sr-only" />
    </AspectRatio>
  )
}

With State Management

function ImageCarousel({ images, currentIndex }) {
  return (
    <AspectRatio ratio={4 / 3} className="bg-muted rounded-lg overflow-hidden">
      <AnimatePresence mode="wait">
        <motion.div
          key={currentIndex}
          initial={{ opacity: 0 }}
          animate={{ opacity: 1 }}
          exit={{ opacity: 0 }}
          className="h-full w-full"
        >
          <Image
            src={images[currentIndex]}
            alt={`Image ${currentIndex + 1}`}
            fill
            className="object-cover"
          />
        </motion.div>
      </AnimatePresence>
    </AspectRatio>
  )
}

SEO Considerations

  1. Image Optimization: Proper aspect ratios improve Core Web Vitals
  2. Lazy Loading: Better performance metrics with Next.js Image
  3. Structured Data: Use appropriate schema markup for images
  4. Alt Text: Descriptive alt attributes improve accessibility and SEO
  5. Responsive Images: Serve appropriate sizes for different devices