Bento Grid
Feature grid with animated mini-visuals in every tile.
Real-time analytics
Watch every metric update the moment it happens — no refresh required.
Building
Optimizing
Deployed
Instant deploys
Push to main and you're live in under 30 seconds.
Enterprise security
Encryption at rest and in transit, with SSO and SCIM.
SlackGitHubLinearNotionStripeFigma
120+ integrations
Connect the tools your team already uses in a few clicks.
Global by default
Multi-region infrastructure keeps latency low for every user, everywhere.
components/ui/bento-grid.tsx
"use client";
import { motion } from "motion/react";
import { BarChart3, Globe2, Lock, Plug, Zap } from "lucide-react";
import type { ReactNode } from "react";
type BentoTileProps = {
icon: ReactNode;
title: string;
text: string;
className?: string;
children?: ReactNode;
index?: number;
};
export function BentoTile({ icon, title, text, className = "", children, index = 0 }: BentoTileProps) {
return (
<motion.div
initial={{ opacity: 0, y: 24 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, amount: 0.3 }}
transition={{ duration: 0.6, delay: index * 0.08, ease: [0.22, 1, 0.36, 1] }}
whileHover={{ y: -4 }}
className={`group relative flex flex-col overflow-hidden rounded-3xl border border-zinc-200 bg-white p-6 shadow-sm transition-shadow hover:shadow-xl hover:shadow-zinc-900/5 ${className}`}
>
<div className="flex-1">{children}</div>
<div className="mt-6">
<div className="flex items-center gap-2 text-zinc-900">
<span className="text-blue-600">{icon}</span>
<h3 className="font-semibold">{title}</h3>
</div>
<p className="mt-1.5 text-sm leading-relaxed text-zinc-500">{text}</p>
</div>
</motion.div>
);
}
export function BentoGrid({ children, className = "" }: { children: ReactNode; className?: string }) {
return <div className={`grid auto-rows-[minmax(220px,auto)] gap-4 md:grid-cols-3 ${className}`}>{children}</div>;
}
const bars = [38, 62, 45, 80, 56, 92, 70];
export default function Demo() {
return (
<BentoGrid className="w-full max-w-5xl">
<BentoTile index={0} className="md:col-span-2" icon={<BarChart3 className="size-4" />} title="Real-time analytics" text="Watch every metric update the moment it happens — no refresh required.">
<div className="flex h-36 items-end gap-2.5">
{bars.map((h, i) => (
<motion.div
key={i}
className="flex-1 rounded-t-lg bg-gradient-to-t from-blue-600 to-blue-400"
initial={{ height: 0 }}
whileInView={{ height: `${h}%` }}
viewport={{ once: true }}
transition={{ duration: 0.9, delay: 0.2 + i * 0.07, ease: [0.22, 1, 0.36, 1] }}
/>
))}
</div>
</BentoTile>
<BentoTile index={1} icon={<Zap className="size-4" />} title="Instant deploys" text="Push to main and you're live in under 30 seconds.">
<div className="space-y-2">
{["Building", "Optimizing", "Deployed"].map((s, i) => (
<motion.div
key={s}
initial={{ opacity: 0, x: -12 }}
whileInView={{ opacity: 1, x: 0 }}
viewport={{ once: true }}
transition={{ delay: 0.3 + i * 0.25 }}
className="flex items-center gap-2 rounded-xl border border-zinc-100 bg-zinc-50 px-3 py-2 text-xs font-medium text-zinc-600"
>
<span className={`size-2 rounded-full ${i === 2 ? "bg-emerald-500" : "bg-blue-500"}`} />
{s}
</motion.div>
))}
</div>
</BentoTile>
<BentoTile index={2} icon={<Lock className="size-4" />} title="Enterprise security" text="Encryption at rest and in transit, with SSO and SCIM.">
<div className="grid h-full place-items-center">
<motion.div
className="grid size-20 place-items-center rounded-2xl bg-zinc-900 text-white shadow-lg"
animate={{ boxShadow: ["0 0 0 0 rgba(37,99,235,0.35)", "0 0 0 18px rgba(37,99,235,0)"] }}
transition={{ duration: 2, repeat: Infinity }}
>
<Lock className="size-8" />
</motion.div>
</div>
</BentoTile>
<BentoTile index={3} icon={<Plug className="size-4" />} title="120+ integrations" text="Connect the tools your team already uses in a few clicks.">
<div className="flex flex-wrap gap-2">
{["Slack", "GitHub", "Linear", "Notion", "Stripe", "Figma"].map((n) => (
<span key={n} className="rounded-full border border-zinc-200 px-3 py-1 text-xs text-zinc-600 transition-colors group-hover:border-blue-200 group-hover:text-blue-700">
{n}
</span>
))}
</div>
</BentoTile>
<BentoTile index={4} icon={<Globe2 className="size-4" />} title="Global by default" text="Multi-region infrastructure keeps latency low for every user, everywhere.">
<div className="grid h-full place-items-center">
<motion.div animate={{ rotate: 360 }} transition={{ duration: 30, repeat: Infinity, ease: "linear" }}>
<Globe2 className="size-24 text-zinc-200" strokeWidth={0.8} />
</motion.div>
</div>
</BentoTile>
</BentoGrid>
);
}
Installation
Install the dependencies
$ npm i motion lucide-reactCopy the source into
components/ui/bento-grid.tsxcomponents/ui/bento-grid.tsx"use client"; import { motion } from "motion/react"; import { BarChart3, Globe2, Lock, Plug, Zap } from "lucide-react"; import type { ReactNode } from "react"; type BentoTileProps = { icon: ReactNode; title: string; text: string; className?: string; children?: ReactNode; index?: number; }; export function BentoTile({ icon, title, text, className = "", children, index = 0 }: BentoTileProps) { return ( <motion.div initial={{ opacity: 0, y: 24 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true, amount: 0.3 }} transition={{ duration: 0.6, delay: index * 0.08, ease: [0.22, 1, 0.36, 1] }} whileHover={{ y: -4 }} className={`group relative flex flex-col overflow-hidden rounded-3xl border border-zinc-200 bg-white p-6 shadow-sm transition-shadow hover:shadow-xl hover:shadow-zinc-900/5 ${className}`} > <div className="flex-1">{children}</div> <div className="mt-6"> <div className="flex items-center gap-2 text-zinc-900"> <span className="text-blue-600">{icon}</span> <h3 className="font-semibold">{title}</h3> </div> <p className="mt-1.5 text-sm leading-relaxed text-zinc-500">{text}</p> </div> </motion.div> ); } export function BentoGrid({ children, className = "" }: { children: ReactNode; className?: string }) { return <div className={`grid auto-rows-[minmax(220px,auto)] gap-4 md:grid-cols-3 ${className}`}>{children}</div>; } const bars = [38, 62, 45, 80, 56, 92, 70]; export default function Demo() { return ( <BentoGrid className="w-full max-w-5xl"> <BentoTile index={0} className="md:col-span-2" icon={<BarChart3 className="size-4" />} title="Real-time analytics" text="Watch every metric update the moment it happens — no refresh required."> <div className="flex h-36 items-end gap-2.5"> {bars.map((h, i) => ( <motion.div key={i} className="flex-1 rounded-t-lg bg-gradient-to-t from-blue-600 to-blue-400" initial={{ height: 0 }} whileInView={{ height: `${h}%` }} viewport={{ once: true }} transition={{ duration: 0.9, delay: 0.2 + i * 0.07, ease: [0.22, 1, 0.36, 1] }} /> ))} </div> </BentoTile> <BentoTile index={1} icon={<Zap className="size-4" />} title="Instant deploys" text="Push to main and you're live in under 30 seconds."> <div className="space-y-2"> {["Building", "Optimizing", "Deployed"].map((s, i) => ( <motion.div key={s} initial={{ opacity: 0, x: -12 }} whileInView={{ opacity: 1, x: 0 }} viewport={{ once: true }} transition={{ delay: 0.3 + i * 0.25 }} className="flex items-center gap-2 rounded-xl border border-zinc-100 bg-zinc-50 px-3 py-2 text-xs font-medium text-zinc-600" > <span className={`size-2 rounded-full ${i === 2 ? "bg-emerald-500" : "bg-blue-500"}`} /> {s} </motion.div> ))} </div> </BentoTile> <BentoTile index={2} icon={<Lock className="size-4" />} title="Enterprise security" text="Encryption at rest and in transit, with SSO and SCIM."> <div className="grid h-full place-items-center"> <motion.div className="grid size-20 place-items-center rounded-2xl bg-zinc-900 text-white shadow-lg" animate={{ boxShadow: ["0 0 0 0 rgba(37,99,235,0.35)", "0 0 0 18px rgba(37,99,235,0)"] }} transition={{ duration: 2, repeat: Infinity }} > <Lock className="size-8" /> </motion.div> </div> </BentoTile> <BentoTile index={3} icon={<Plug className="size-4" />} title="120+ integrations" text="Connect the tools your team already uses in a few clicks."> <div className="flex flex-wrap gap-2"> {["Slack", "GitHub", "Linear", "Notion", "Stripe", "Figma"].map((n) => ( <span key={n} className="rounded-full border border-zinc-200 px-3 py-1 text-xs text-zinc-600 transition-colors group-hover:border-blue-200 group-hover:text-blue-700"> {n} </span> ))} </div> </BentoTile> <BentoTile index={4} icon={<Globe2 className="size-4" />} title="Global by default" text="Multi-region infrastructure keeps latency low for every user, everywhere."> <div className="grid h-full place-items-center"> <motion.div animate={{ rotate: 360 }} transition={{ duration: 30, repeat: Infinity, ease: "linear" }}> <Globe2 className="size-24 text-zinc-200" strokeWidth={0.8} /> </motion.div> </div> </BentoTile> </BentoGrid> ); }Use it
The file exports the component and a
Demoshowing typical usage. Import whichever you need.