AspectRatio
Um componente container que impõe uma proporção fixa de largura para altura aos seus filhos, garantindo que imagens, vídeos e outras mídias mantenham proporções consistentes, independentemente da largura do container.
Busque em todas as páginas da documentação
Um componente container que impõe uma proporção fixa de largura para altura aos seus filhos, garantindo que imagens, vídeos e outras mídias mantenham proporções consistentes, independentemente da largura do container.
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
interface AspectRatioProps {
ratio?: number;
children: React.ReactNode;
}
export function AspectRatio({ ratio = 16 / 9, children }: AspectRatioProps) {
return (
<div className="relative w-full" style={{ paddingBottom: `${(1 / ratio) * 100}%` }}>
<div className="absolute inset-0">{children}</div>
</div>
);
}Utiliza o truque clássico de padding-bottom para estabelecer a altura a partir da largura. O container interno absolute inset-0 preenche o espaço reservado e contém o conteúdo filho. Não é necessário "use client", pois não há interatividade.
interface AspectRatioProps {
children: React.ReactNode;
className?: string;
}
export function AspectRatio16x9({ children, className }: AspectRatioProps) {
return (
<div className={`relative aspect-video overflow-hidden rounded-lg ${className ?? ""}`}>
{children}
</div>
);
}
// Uso
<AspectRatio16x9>
<img src="/hero.jpg" alt="Hero" className="h-full w-full object-cover" />
</AspectRatio16x9>A utility aspect-video do Tailwind aplica aspect-ratio: 16 / 9 nativamente. O overflow-hidden com rounded-lg recorta o conteúdo filho para cantos arredondados. Os filhos devem usar h-full w-full object-cover para preencher o frame sem distorção.
interface AspectRatioProps {
children: React.ReactNode;
className?: string;
}
export function AspectRatio4x3({ children, className }: AspectRatioProps) {
return (
<div className={`relative overflow-hidden rounded-lg ${className ?? ""}`} style={{ aspectRatio: "4 / 3" }}>
{children}
</div>
);
}
// Uso
<AspectRatio4x3>
<img src="/photo.jpg" alt="Photo" className="h-full w-full object-cover" />
</AspectRatio4x3>O Tailwind não vem com uma classe aspect-4/3 integrada por padrão, então o estilo inline aspectRatio é a solução mais limpa sem estender a configuração. A proporção 4:3 é adequada para fotografias, imagens de produtos e conteúdo de exibição tradicional.
interface AspectRatioProps {
children: React.ReactNode;
className?: string;
}
export function AspectRatioSquare({ children, className }: AspectRatioProps) {
return (
<div className={`relative aspect-square overflow-hidden rounded-lg ${className ?? ""}`}>
{children}
</div>
);
}
// Uso
<AspectRatioSquare className="w-24">
<img src="/avatar.jpg" alt="User avatar" className="h-full w-full object-cover" />
</AspectRatioSquare>Usa aspect-square do Tailwind para uma proporção 1:1. Ideal para imagens de perfil, miniaturas de produtos em grades quadradas e pré-visualizações de posts de redes sociais. Defina uma largura no container externo e a altura seguirá automaticamente.
import Image from "next/image";
interface AspectRatioImageProps {
src: string;
alt: string;
ratio?: number;
className?: string;
}
export function AspectRatioImage({
src,
alt,
ratio = 16 / 9,
className,
}: AspectRatioImageProps) {
return (
<div
className={`relative overflow-hidden rounded-lg ${className ?? ""}`}
style={{ aspectRatio: String(ratio) }}
>
<Image
src={src}
alt={alt}
fill
sizes="(max-width: 768px) 100vw, 50vw"
className="object-cover"
/>
</div>
);
}
// Uso
<AspectRatioImage src="/banner.jpg" alt="Banner" ratio={21 / 9} />Combina o container de proporção com o componente Image do Next.js no modo fill. A prop sizes é crucial para o desempenho -- ela informa ao navegador qual tamanho de imagem baixar em cada largura de viewport.
interface AspectRatioVideoProps {
src: string;
title: string;
className?: string;
}
export function AspectRatioVideo({ src, title, className }: AspectRatioVideoProps) {
return (
<div className={`relative aspect-video overflow-hidden rounded-lg ${className ?? ""}`}>
<iframe
src={src}
title={title}
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
className="absolute inset-0 h-full w-full border-0"
/>
</div>
);
}
// Uso
<AspectRatioVideo
src="https://www.youtube.com/embed/dQw4w9WgXcQ"
title="Video tutorial"
/>O iframe é posicionado absolutamente dentro do container de proporção para que se estenda e preencha as dimensões exatas. O border-0 remove a borda padrão do iframe. Sempre inclua um title descritivo para acessibilidade.
interface AspectRatioProps {
ratio: number;
children: React.ReactNode;
className?: string;
}
export function AspectRatio({ ratio, children, className }: AspectRatioProps) {
return (
<div
className={`relative overflow-hidden ${className ?? ""}`}
style={{ aspectRatio: String(ratio) }}
>
{children}
</div>
);
}
// Exemplos de uso
<AspectRatio ratio={21 / 9} className="rounded-lg">
<img src="/ultrawide.jpg" alt="Ultrawide" className="h-full w-full object-cover" />
</AspectRatio>
<AspectRatio ratio={3 / 4} className="rounded-lg">
<img src="/portrait.jpg" alt="Portrait" className="h-full w-full object-cover" />
</AspectRatio>Aceita qualquer proporção numérica para dimensões não padrão. A prop ratio é uma divisão simples (largura / altura), tornando-a intuitiva: 21/9 para ultrawide, 3/4 para retrato, 2/1 para um banner largo.
import { forwardRef, type CSSProperties } from "react";
import Image from "next/image";
type PresetRatio = "square" | "video" | "photo" | "portrait" | "ultrawide";
interface AspectRatioProps {
ratio?: number | PresetRatio;
children: React.ReactNode;
maxHeight?: number;
className?: string;
style?: CSSProperties;
}
const presetRatios: Record<PresetRatio, number> = {
square: 1,
video: 16 / 9,
photo: 4 / 3,
portrait: 3 / 4,
ultrawide: 21 / 9,
};
function resolveRatio(ratio: number | PresetRatio): number {
return typeof ratio === "string" ? presetRatios[ratio] : ratio;
}
export const AspectRatio = forwardRef<HTMLDivElement, AspectRatioProps>(
function AspectRatio({ ratio = "video", children, maxHeight, className, style }, ref) {
const numericRatio = resolveRatio(ratio);
return (
<div
ref={ref}
className={`relative overflow-hidden ${className ?? ""}`}
style={{
aspectRatio: String(numericRatio),
maxHeight: maxHeight ? `${maxHeight}px` : undefined,
...style,
}}
>
{children}
</div>
);
}
);
// Componente complementar para o padrão comum de imagem em proporção
interface AspectRatioImageProps {
ratio?: number | PresetRatio;
src: string;
alt: string;
priority?: boolean;
sizes?: string;
maxHeight?: number;
className?: string;
imageClassName?: string;
}
export const AspectImage = forwardRef<HTMLDivElement, AspectRatioImageProps>(
function AspectImage(
{
ratio = "video",
src,
alt,
priority = false,
sizes = "(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw",
maxHeight,
className,
imageClassName,
},
ref
) {
return (
<AspectRatio
ref={ref}
ratio={ratio}
maxHeight={maxHeight}
className={`bg-gray-100 ${className ?? ""}`}
>
<Image
src={src}
alt={alt}
fill
priority={priority}
sizes={sizes}
className={`object-cover ${imageClassName ?? ""}`}
/>
</AspectRatio>
);
}
);
// Uso
function Gallery() {
const images = [
{ src: "/img1.jpg", alt: "Mountain landscape" },
{ src: "/img2.jpg", alt: "City skyline" },
{ src: "/img3.jpg", alt: "Ocean sunset" },
];
return (
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
{images.map((img) => (
<AspectImage
key={img.src}
ratio="photo"
src={img.src}
alt={img.alt}
className="rounded-xl"
/>
))}
</div>
);
}Aspectos Chave:
"video", "photo" e "portrait" eliminam a necessidade de lembrar valores numéricos e melhoram a legibilidade do código no local de chamada.2.35 (cinemascope) sem estender o mapa de predefinições.maxHeight -- impede que o container de proporção cresça demais em telas largas. Quando a altura máxima é atingida, a largura se torna a dimensão restrita.AspectImage Complementar -- um componente separado envolve o padrão comum de Image do Next.js dentro de uma caixa de proporção, fornecendo padrões sensatos para sizes e uma área de placeholder cinza.forwardRef em ambos os componentes -- refs fluem para o DOM para que observadores de interseção, bibliotecas de animação ou efeitos vinculados à rolagem possam se anexar ao container.bg-gray-100 em AspectImage fornece uma área de esqueleto visível enquanto a imagem carrega, prevenindo um espaço invisível antes da primeira pintura.Propriedade CSS aspect-ratio não suportada em versões antigas do Safari -- versões do Safari anteriores à 15 não suportam a propriedade nativa aspect-ratio. Se você precisar dar suporte a esses navegadores, use o hack de padding-bottom.
Next.js Image com fill requer um pai posicionado -- o componente Image no modo fill usa position: absolute, então o container deve ter position: relative. Esquecer isso faz com que a imagem saia do container.
Incompatibilidade object-cover vs object-contain -- usar object-cover recorta a imagem para preencher o frame, enquanto object-contain adiciona barras pretas. Escolher o errado corta conteúdo importante ou deixa espaços feios.
Layout shift quando a proporção é carregada dinamicamente -- se a proporção vem de um CMS ou API e não é conhecida no momento da compilação, o container renderiza sem altura até que o JavaScript hidrate. Inclua a proporção no HTML renderizado pelo servidor ou use uma proporção de fallback.
Prop sizes ausente no Next.js Image -- sem sizes, o Next.js usa 100vw por padrão, fazendo com que o navegador baixe uma imagem muito maior do que o necessário em viewports pequenas. Sempre calcule breakpoints sizes realistas.
Conflito de padding-based percentage e flexbox -- o truque de percentual de padding-bottom calcula em relação à largura do pai, mas dentro de uma coluna flexível, a largura de referência pode ser inesperada. A propriedade CSS nativa aspect-ratio evita completamente esse problema.
Revisado por Chris St. John·Última atualização: 10 de jul. de 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥