AspectRatio
Un componente contenedor que aplica una relación ancho-alto fija para sus hijos, asegurando que imágenes, videos y otro contenido multimedia mantengan proporciones consistentes independientemente del ancho del contenedor.
Busca en todas las páginas de la documentación
Un componente contenedor que aplica una relación ancho-alto fija para sus hijos, asegurando que imágenes, videos y otro contenido multimedia mantengan proporciones consistentes independientemente del ancho del contenedor.
🤖 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 el clásico truco padding-bottom para establecer la altura desde el ancho. El contenedor absolute inset-0 interno llena el espacio reservado y contiene el contenido hijo. No se necesita "use client" ya que no hay interactividad.
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>La utilidad aspect-video de Tailwind aplica aspect-ratio: 16 / 9 nativamente. El overflow-hidden con rounded-lg recorta el contenido hijo a esquinas redondeadas. Los hijos deben usar h-full w-full object-cover para llenar el marco sin distorsión.
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>Tailwind no proporciona una clase aspect-4/3 integrada por defecto, por lo que el estilo aspectRatio en línea es la solución más limpia sin extender la configuración. La relación 4:3 es adecuada para fotografías, imágenes de productos y contenido de pantalla 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>Utiliza aspect-square de Tailwind para una relación 1:1. Ideal para imágenes de perfil, miniaturas de productos en cuadrículas cuadradas y vistas previas de publicaciones en redes sociales. Establece un ancho en el contenedor externo y la altura sigue automáticamente.
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 el contenedor de relación de aspecto con el componente Image de Next.js en modo fill. El prop sizes es crítico para el rendimiento -- le dice al navegador qué tamaño de imagen descargar en cada ancho 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"
/>El iframe se posiciona absolutamente dentro del contenedor de relación de aspecto para que se estire y llene las dimensiones exactas. El border-0 elimina el borde predeterminado del iframe. Siempre incluye un title descriptivo para accesibilidad.
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>
);
}
// Ejemplos 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>Acepta cualquier relación numérica para dimensiones no estándar. El prop ratio es una división simple (width / height), lo que la hace intuitiva: 21/9 para ultraancho, 3/4 para retrato, 2/1 para un banner ancho.
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 complementario para patrón común imagen-en-ratio
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 clave:
"video", "photo" y "portrait" eliminan la necesidad de recordar valores numéricos y mejoran la legibilidad del código en el sitio de llamada.2.35 (cinemascope) se cubren sin extender el mapa de preestablecidos.Image de Next.js dentro de una caja de relación de aspecto, proporcionando valores sizes predeterminados sensatos y un fondo de placeholder gris.bg-gray-100 en AspectImage proporciona un área de esqueleto visible mientras la imagen se carga, previniendo una brecha invisible antes del primer pintado.Propiedad CSS aspect-ratio no soportada en Safari antiguo -- Versiones de Safari anteriores a 15 no soportan la propiedad aspect-ratio nativa. Si necesitas soportar esos navegadores, usa el hack padding-bottom en su lugar.
Next.js Image con fill requiere un padre posicionado -- el componente Image en modo fill usa position: absolute, por lo que el contenedor debe tener position: relative. Olvidar esto hace que la imagen se rompa fuera del contenedor.
Desajuste object-cover vs object-contain -- usar object-cover recorta la imagen para llenar el marco, mientras que object-contain la cuadra con letterboxing. Elegir la incorrecta recorta contenido importante o deja espacios feos.
Layout shift cuando la relación se carga dinámicamente -- si la relación viene de un CMS o API y no se conoce en tiempo de compilación, el contenedor se renderiza sin altura hasta que JavaScript hidrata. Incluye la relación en el HTML renderizado en servidor o usa una relación alternativa.
Prop sizes faltante en Next.js Image -- sin sizes, Next.js usa por defecto 100vw, causando que el navegador descargue una imagen mucho más grande de la necesaria en viewports pequeños. Siempre calcula puntos de quiebre sizes realistas.
Conflicto del truco padding porcentual y flexbox -- el truco padding-bottom porcentual se calcula relativo al ancho del padre, pero dentro de una columna flex la referencia de ancho puede ser inesperada. La propiedad CSS aspect-ratio nativa evita completamente este problema.
Revisado por Chris St. John·Última actualización: 10 jul 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥