We use tracking cookies to understand how you use the product and help us improve it. For more information on how we store cookies, read our  privacy policy.

BentoGrid.tsx

Modern grid layout component for showcasing features with interactive cards

The Bento Grid component creates a modern, Pinterest-style grid layout with interactive cards that reveal call-to-action buttons on hover.

Features

  • Responsive grid layout
  • Interactive hover effects
  • Background media support
  • Icon integration with SvgFinder
  • Smooth animations
  • Customizable card sizes

Basic Usage

app/page.tsx
import { BentoGrid, BentoCard } from '@/components/ui/BentoGrid';

export default function Page() {
  return (
    <BentoGrid>
      <BentoCard
        name="Feature 1"
        className="col-span-3 lg:col-span-1"
        background={<div className="absolute inset-0 bg-gradient-to-br from-brand/20 to-brand/5" />}
        icon="lockIcon"
        description="Secure authentication with Clerk"
        href="/docs/core/authentication"
        cta="Learn more"
      />
      
      <BentoCard
        name="Feature 2"
        className="col-span-3 lg:col-span-2"
        background={<div className="absolute inset-0 bg-gradient-to-br from-purple-500/20 to-purple-500/5" />}
        icon="walletIcon"
        description="Accept payments with Stripe"
        href="/docs/core/payments"
        cta="Get started"
      />
    </BentoGrid>
  );
}

With Images

app/page.tsx
import { BentoGrid, BentoCard } from '@/components/ui/BentoGrid';
import Image from 'next/image';

<BentoGrid>
  <BentoCard
    name="Dashboard"
    className="col-span-3 lg:col-span-2"
    background={
      <Image
        src="/screenshots/dashboard.png"
        alt="Dashboard"
        fill
        className="object-cover opacity-60"
      />
    }
    icon="chartIcon"
    description="Powerful analytics dashboard"
    href="/dashboard"
    cta="View dashboard"
  />
</BentoGrid>

Custom Grid Layout

app/page.tsx
<BentoGrid className="grid-cols-1 md:grid-cols-2 lg:grid-cols-4">
  <BentoCard
    name="Small Card"
    className="col-span-1"
    background={<div className="bg-blue-500/10" />}
    icon="starIcon"
    description="Small feature card"
    href="/feature-1"
    cta="Explore"
  />
  
  <BentoCard
    name="Wide Card"
    className="col-span-1 md:col-span-2"
    background={<div className="bg-green-500/10" />}
    icon="rocketIcon"
    description="Wide feature card"
    href="/feature-2"
    cta="Discover"
  />
  
  <BentoCard
    name="Tall Card"
    className="col-span-1 row-span-2"
    background={<div className="bg-purple-500/10" />}
    icon="zap Icon"
    description="Tall feature card"
    href="/feature-3"
    cta="Learn more"
  />
</BentoGrid>

With Video Background

app/page.tsx
<BentoCard
  name="Video Demo"
  className="col-span-3"
  background={
    <video
      autoPlay
      loop
      muted
      playsInline
      className="absolute inset-0 w-full h-full object-cover opacity-50"
    >
      <source src="/videos/demo.mp4" type="video/mp4" />
    </video>
  }
  icon="playIcon"
  description="Watch our product in action"
  href="/demo"
  cta="Watch demo"
/>

Feature Showcase

app/page.tsx
import { BentoGrid, BentoCard } from '@/components/ui/BentoGrid';

export default function Features() {
  const features = [
    {
      name: 'Authentication',
      icon: 'lockIcon',
      description: 'Secure user authentication with Clerk',
      href: '/docs/core/authentication',
      cta: 'Learn more',
      background: <div className="bg-gradient-to-br from-blue-500/20 to-blue-500/5" />,
      className: 'col-span-3 lg:col-span-1',
    },
    {
      name: 'Payments',
      icon: 'walletIcon',
      description: 'Accept payments with Stripe integration',
      href: '/docs/core/payments',
      cta: 'Get started',
      background: <div className="bg-gradient-to-br from-green-500/20 to-green-500/5" />,
      className: 'col-span-3 lg:col-span-2',
    },
    {
      name: 'Database',
      icon: 'databaseIcon',
      description: 'PostgreSQL with Prisma ORM',
      href: '/docs/core/database',
      cta: 'Explore',
      background: <div className="bg-gradient-to-br from-purple-500/20 to-purple-500/5" />,
      className: 'col-span-3 lg:col-span-2',
    },
    {
      name: 'Email',
      icon: 'emailIcon',
      description: 'Transactional emails with Resend',
      href: '/docs/core/emails',
      cta: 'View docs',
      background: <div className="bg-gradient-to-br from-orange-500/20 to-orange-500/5" />,
      className: 'col-span-3 lg:col-span-1',
    },
  ];

  return (
    <BentoGrid>
      {features.map((feature) => (
        <BentoCard key={feature.name} {...feature} />
      ))}
    </BentoGrid>
  );
}

Implementation

The BentoCard includes hover animations:

components/ui/BentoGrid.tsx
const BentoCard = ({
  name,
  className,
  background,
  icon,
  description,
  href,
  cta,
}: IBentoCardProps) => (
  <div
    className={cn(
      'group relative flex flex-col justify-between overflow-hidden rounded-xl bg-surface',
      '[box-shadow:0_0_0_1px_rgba(0,0,0,.03),0_2px_4px_rgba(0,0,0,.05)]',
      'dark:[box-shadow:0_-20px_80px_-20px_#ffffff1f_inset]',
      className
    )}
  >
    <div>{background}</div>
    <div className="p-4">
      <div className="transform-gpu transition-all duration-300 lg:group-hover:-translate-y-10">
        <SvgFinder
          icon={icon}
          size={38}
          className="transform-gpu transition-all duration-300 group-hover:scale-75"
        />
        <h3 className="text-xl font-semibold">{name}</h3>
        <p className="text-neutral-foreground">{description}</p>
      </div>

      <div className="transform-gpu transition-all duration-300 group-hover:translate-y-0 group-hover:opacity-100 lg:hidden">
        <Button variant="link" asChild>
          <a href={href}>{cta}</a>
        </Button>
      </div>
    </div>
  </div>
);

Props

BentoGrid

interface IBentoGridProps {
  children: ReactNode;
  className?: string;
}

BentoCard

interface IBentoCardProps {
  name: string;              // Card title
  className: string;         // Grid positioning classes
  background: ReactNode;     // Background element (image, video, gradient)
  icon: string;              // Icon name for SvgFinder
  description: string;       // Card description
  href: string;              // Link destination
  cta: string;               // Call-to-action text
}

Responsive Behavior

  • Mobile: Single column, CTA always visible
  • Desktop: Multi-column grid, CTA appears on hover
  • Smooth transitions between breakpoints

Best Practices

  • Use descriptive card names
  • Keep descriptions concise (2-3 lines)
  • Choose appropriate grid spans for content
  • Use consistent background styles
  • Ensure sufficient contrast for readability
  • Test hover interactions on desktop

How is this guide ?

Last updated on