Los bento grids están por todas partes: Apple los usa en sus páginas de producto, Linear en su changelog, Vercel en su dashboard. El atractivo es evidente: los layouts asimétricos de tarjetas generan un interés visual que las cuadrículas uniformes nunca logran. Cada tarjeta recibe exactamente el espacio que su contenido necesita, y la composición global se siente cuidada en lugar de prefabricada.
Esta guía recorre seis patrones de bento grid en React con Tailwind CSS, desde un layout básico hasta variantes animadas e interactivas. Todos están listos para copiar y pegar.
1. Bento Grid básico
La base: un CSS Grid con grid-template-columns y utilidades de spanning. El layout parte de una cuadrícula base de 4 columnas donde las tarjetas ocupan 1 o 2 columnas y filas:
export function BentoBasic() {
return (
<section className="mx-auto max-w-6xl px-4 py-24">
<h2 className="mb-12 text-center text-3xl font-bold">Why teams choose us</h2>
<div className="grid grid-cols-2 gap-4 md:grid-cols-4">
{/* Large card, 2x2 */}
<div className="col-span-2 row-span-2 rounded-2xl border border-border bg-card p-8">
<h3 className="text-2xl font-bold">Lightning fast</h3>
<p className="mt-2 text-muted-foreground">
Sub-100ms response times on every interaction.
</p>
</div>
{/* Small cards, 1x1 */}
<div className="rounded-2xl border border-border bg-card p-6">
<h3 className="font-semibold">99.9% uptime</h3>
</div>
<div className="rounded-2xl border border-border bg-card p-6">
<h3 className="font-semibold">SOC 2 certified</h3>
</div>
<div className="rounded-2xl border border-border bg-card p-6">
<h3 className="font-semibold">50+ integrations</h3>
</div>
<div className="rounded-2xl border border-border bg-card p-6">
<h3 className="font-semibold">24/7 support</h3>
</div>
</div>
</section>
);
}
La clave está en col-span-2 row-span-2 sobre la tarjeta principal. Ocupa 4 veces el área de una tarjeta normal y crea la asimetría característica del bento.
2. Layout asimétrico
Una composición más editorial donde la cuadrícula cambia de forma de una fila a otra. En vez de una base uniforme, cada fila define su propio reparto de columnas:
export function BentoAsymmetric() {
return (
<div className="mx-auto grid max-w-6xl gap-4 px-4 py-24">
{/* Row 1: 60/40 split */}
<div className="grid grid-cols-1 gap-4 md:grid-cols-[1.5fr_1fr]">
<div className="rounded-2xl border border-border bg-card p-8">
<h3 className="text-xl font-bold">Real-time collaboration</h3>
<p className="mt-2 text-muted-foreground">
See changes as your team makes them. No refresh needed.
</p>
</div>
<div className="rounded-2xl border border-border bg-card p-8">
<h3 className="text-xl font-bold">Version history</h3>
<p className="mt-2 text-muted-foreground">Every change tracked.</p>
</div>
</div>
{/* Row 2: 40/60 split (inverted) */}
<div className="grid grid-cols-1 gap-4 md:grid-cols-[1fr_1.5fr]">
<div className="rounded-2xl border border-border bg-card p-8">
<h3 className="text-xl font-bold">API access</h3>
<p className="mt-2 text-muted-foreground">Full REST + GraphQL API.</p>
</div>
<div className="rounded-2xl border border-border bg-card p-8">
<h3 className="text-xl font-bold">Advanced analytics</h3>
<p className="mt-2 text-muted-foreground">
Custom dashboards, exports, and scheduled reports.
</p>
</div>
</div>
{/* Row 3: equal thirds */}
<div className="grid grid-cols-1 gap-4 md:grid-cols-3">
<div className="rounded-2xl border border-border bg-card p-6 text-center">
<p className="text-3xl font-bold">10M+</p>
<p className="text-sm text-muted-foreground">Requests per day</p>
</div>
<div className="rounded-2xl border border-border bg-card p-6 text-center">
<p className="text-3xl font-bold">99.99%</p>
<p className="text-sm text-muted-foreground">Uptime SLA</p>
</div>
<div className="rounded-2xl border border-border bg-card p-6 text-center">
<p className="text-3xl font-bold">150+</p>
<p className="text-sm text-muted-foreground">Countries served</p>
</div>
</div>
</div>
);
}
Este enfoque resulta más fácil de razonar que el spanning dentro de una sola cuadrícula, porque cada fila es independiente. Además se colapsa con elegancia a una sola columna en móvil.
3. Bento con reveal animado
Combina la cuadrícula básica con animaciones de entrada escalonadas de Framer Motion:
"use client";
import { motion } from "motion/react";
const container = {
hidden: {},
visible: { transition: { staggerChildren: 0.06 } },
};
const card = {
hidden: { opacity: 0, scale: 0.95, y: 12 },
visible: {
opacity: 1,
scale: 1,
y: 0,
transition: { duration: 0.4, ease: [0.16, 1, 0.3, 1] },
},
};
export function BentoAnimated({ items }: { items: { title: string; span?: string }[] }) {
return (
<motion.div
variants={container}
initial="hidden"
whileInView="visible"
viewport={{ once: true, margin: "-80px" }}
className="mx-auto grid max-w-6xl grid-cols-2 gap-4 px-4 md:grid-cols-4"
>
{items.map((item) => (
<motion.div
key={item.title}
variants={card}
className={`rounded-2xl border border-border bg-card p-6 ${item.span ?? ""}`}
>
<h3 className="font-semibold">{item.title}</h3>
</motion.div>
))}
</motion.div>
);
}
Pasa span: "col-span-2 row-span-2" en los items que deban ser más grandes. El retardo de stagger de 0.06s produce un efecto cascada rápido que se siente dinámico sin volverse lento.
4. Bento arrastrable
Una variante interactiva en la que el usuario puede reordenar las tarjetas. Aquí entra el componente Reorder de Framer Motion:
"use client";
import { Reorder } from "motion/react";
import { useState } from "react";
interface BentoItem {
id: string;
title: string;
description: string;
}
export function BentoDraggable({ initialItems }: { initialItems: BentoItem[] }) {
const [items, setItems] = useState(initialItems);
return (
<Reorder.Group
axis="y"
values={items}
onReorder={setItems}
className="mx-auto grid max-w-4xl gap-4 px-4"
>
{items.map((item) => (
<Reorder.Item
key={item.id}
value={item}
className="cursor-grab rounded-2xl border border-border bg-card p-6 active:cursor-grabbing"
>
<h3 className="font-semibold">{item.title}</h3>
<p className="mt-1 text-sm text-muted-foreground">{item.description}</p>
</Reorder.Item>
))}
</Reorder.Group>
);
}
El bento arrastrable es ideal para dashboards, flujos de onboarding o demos interactivas en landing pages donde quieres mostrar lo personalizable que es el producto.
5. Bento con neon glow
Para landing pages con tema oscuro, añade un sutil efecto de brillo en los bordes de las tarjetas al pasar el cursor:
export function BentoNeon() {
return (
<div className="grid grid-cols-2 gap-4 md:grid-cols-4">
<div
className="group relative col-span-2 row-span-2 overflow-hidden rounded-2xl border border-neutral-800 bg-neutral-950 p-8 transition-colors hover:border-blue-500/50"
>
{/* Glow effect */}
<div className="pointer-events-none absolute inset-0 opacity-0 transition-opacity group-hover:opacity-100">
<div className="absolute -inset-1 bg-blue-500/10 blur-xl" />
</div>
<div className="relative">
<h3 className="text-xl font-bold text-white">AI-Powered Search</h3>
<p className="mt-2 text-neutral-400">
Natural language queries across your entire workspace.
</p>
</div>
</div>
{/* Additional cards with different glow colors */}
<div className="group relative overflow-hidden rounded-2xl border border-neutral-800 bg-neutral-950 p-6 transition-colors hover:border-purple-500/50">
<div className="pointer-events-none absolute inset-0 opacity-0 transition-opacity group-hover:opacity-100">
<div className="absolute -inset-1 bg-purple-500/10 blur-xl" />
</div>
<div className="relative">
<h3 className="font-semibold text-white">Automation</h3>
</div>
</div>
<div className="group relative overflow-hidden rounded-2xl border border-neutral-800 bg-neutral-950 p-6 transition-colors hover:border-green-500/50">
<div className="pointer-events-none absolute inset-0 opacity-0 transition-opacity group-hover:opacity-100">
<div className="absolute -inset-1 bg-green-500/10 blur-xl" />
</div>
<div className="relative">
<h3 className="font-semibold text-white">Integrations</h3>
</div>
</div>
</div>
);
}
El brillo es un div desenfocado con baja opacidad que aparece al pasar el cursor. El patrón group / group-hover mantiene el efecto declarativo, sin necesidad de JavaScript.
6. Bento texto + imagen
Un layout híbrido que mezcla tarjetas de texto con imágenes a sangre completa:
export function BentoMedia() {
return (
<div className="mx-auto grid max-w-6xl grid-cols-2 gap-4 px-4 md:grid-cols-4">
{/* Text card, 2 columns */}
<div className="col-span-2 rounded-2xl border border-border bg-card p-8">
<h3 className="text-xl font-bold">Designed for developers</h3>
<p className="mt-2 text-muted-foreground">
First-class TypeScript support, comprehensive API docs, and a CLI that stays out of your way.
</p>
</div>
{/* Image card, 2 columns */}
<div className="col-span-2 overflow-hidden rounded-2xl border border-border">
<img
src="/dashboard-screenshot.png"
alt="Dashboard screenshot"
className="h-full w-full object-cover"
/>
</div>
{/* Full-width image row */}
<div className="col-span-2 overflow-hidden rounded-2xl border border-border md:col-span-4">
<img
src="/workflow-diagram.png"
alt="Workflow automation diagram"
className="h-64 w-full object-cover"
/>
</div>
</div>
);
}
La combinación de tarjetas de texto e imagen rompe la monotonía visual y te deja mostrar el producto en contexto junto a las descripciones de cada feature.
Explora más layouts de bento
Estos seis patrones cubren los casos de uso más habituales de los bento grids, pero hay muchas más variantes: fondos con gradientes, layouts cargados de iconos, contadores de estadísticas y demos interactivas. El catálogo de Incubator tiene una colección dedicada a secciones de bento grid, todas construidas con Tailwind CSS y listas para copiar en tu proyecto React. Cada variante incluye comportamiento responsive, soporte para dark mode y animaciones opcionales con Framer Motion. Explora el catálogo completo para encontrar el layout que encaje con tu landing page.
Relacionado en incubator
- Secciones de bento grid: layouts asimétricos de tarjetas listos para usar en React.
- Explorador de efectos hover: patrones interactivos de hover sobre tarjetas para copiar.
- Explorador de interacciones de arrastre: componentes de UI arrastrables construidos con Framer Motion.
- Secciones de features: layouts para destacar funcionalidades que combinan bien con los bento grids.
- Incubator vs Aceternity UI: mira cómo se comparan las secciones animadas.