Logo
Back
Manu Arora

Manu Arora

How to create a bento grid with Tailwind CSS, Next.js and Framer Motion

How to create a bento grid with Tailwind CSS, Next.js and Framer Motion

A Bento grid is a design trend that has picked up recently where one displays a grid of cards that showcase various features of your product or business.

Recently, we released a brand new template at Aceternity that focuses on marketing. There, we happened to create a bento grid that focuses on displaying the features of the product in a minimalistic and clean manner.

Hero Image Section For AI SaaS Template

Card Structure

To design a bento grid, you need to have a card structure. The card structure is simple with compound components.

For a Card compound component, we are going to require 4 components

  • Card
  • CardTitle
  • CardDescription
  • CardSkeletonContainer
card.tsx
export const Card = ({
  className,
  children,
}: {
  className?: string;
  children: React.ReactNode;
}) => {
  return (
    <div
      className={cn(
        "p-8 rounded-xl border border-[rgba(255,255,255,0.10)] bg-[rgba(40,40,40,0.30)] shadow-[2px_4px_16px_0px_rgba(248,248,248,0.06)_inset] group",
        className
      )}
    >
      {children}
    </div>
  );
};
 
export const CardTitle = ({
  children,
  className,
}: {
  children: React.ReactNode;
  className?: string;
}) => {
  return (
    <h3 className={cn("text-lg font-semibold text-white py-2", className)}>
      {children}
    </h3>
  );
};
 
export const CardDescription = ({
  children,
  className,
}: {
  children: React.ReactNode;
  className?: string;
}) => {
  return (
    <p
      className={cn("text-sm font-normal text-neutral-400 max-w-sm", className)}
    >
      {children}
    </p>
  );
};
 
export const CardSkeletonContainer = ({
  className,
  children,
  showGradient = true,
}: {
  className?: string;
  children: React.ReactNode;
  showGradient?: boolean;
}) => {
  return (
    <div
      className={cn(
        "h-[20rem] rounded-xl z-40",
        className,
        showGradient &&
          " bg-[rgba(40,40,40,0.30)] [mask-image:radial-gradient(50%_50%_at_50%_50%,white_0%,transparent_100%)]"
      )}
    >
      {children}
    </div>
  );
};

Now since we have a card structure in place, let's see how to use it with some data and see how it looks on the UI.

Hero Image Section For AI SaaS Template
first-card.tsx
export const FirstCard = () => {
  return (
    <Card>
      <CardSkeletonContainer>
        <SkeletonTwo />
      </CardSkeletonContainer>
      <CardTitle>Analytics for everything</CardTitle>
      <CardDescription>
        Check analytics, track your posts, and get insights into your audience.
      </CardDescription>
    </Card>
  );
};

Now since we have the card in place, let's see how we can create a Bento Grid with this.

We can use col-span utilities provided by Tailwind CSS to span a card across multiple columns.

For example, if we have a grid of 4 columns and we want to span 1 card across 3 columns and 1 card across 1 column, we can do something like this

example.tsx
export const Example = () => {
  return (
    <div className="grid grid-cols-4 gap-4">
      <Card className="col-span-3">
        <CardSkeletonContainer>
          <SkeletonTwo />
        </CardSkeletonContainer>
        <CardTitle>Analytics for everything</CardTitle>
        <CardDescription>
          Check analytics, track your posts, and get insights into your
          audience.
        </CardDescription>
      </Card>
      <Card>
        <CardSkeletonContainer>
          <SkeletonTwo />
        </CardSkeletonContainer>
        <CardTitle>Analytics for everything</CardTitle>
        <CardDescription>
          Check analytics, track your posts, and get insights into your
          audience.
        </CardDescription>
      </Card>
    </div>
  );
};

Here's how the Bento row looks like once we have col-span-3 in the first card.

Hero Image Section For AI SaaS Template

Now, we can replicate the exact same thing to the second row to create a similar layout

example.tsx
export const Example = () => {
  return (
    <div className="grid grid-cols-4 gap-4">
      <Card className="col-span-3">
        <CardSkeletonContainer>
          <SkeletonTwo />
        </CardSkeletonContainer>
        <CardTitle>Analytics for everything</CardTitle>
        <CardDescription>
          Check analytics, track your posts, and get insights into your
          audience.
        </CardDescription>
      </Card>
      <Card>
        <CardSkeletonContainer>
          <SkeletonTwo />
        </CardSkeletonContainer>
        <CardTitle>Analytics for everything</CardTitle>
        <CardDescription>
          Check analytics, track your posts, and get insights into your
          audience.
        </CardDescription>
      </Card>
      <Card>
        <CardSkeletonContainer>
          <SkeletonTwo />
        </CardSkeletonContainer>
        <CardTitle>Analytics for everything</CardTitle>
        <CardDescription>
          Check analytics, track your posts, and get insights into your
          audience.
        </CardDescription>
      </Card>
      <Card className="col-span-3">
        <CardSkeletonContainer>
          <SkeletonTwo />
        </CardSkeletonContainer>
        <CardTitle>Analytics for everything</CardTitle>
        <CardDescription>
          Check analytics, track your posts, and get insights into your
          audience.
        </CardDescription>
      </Card>
    </div>
  );
};

The output looks something like this after we have the above code in place.

Hero Image Section For AI SaaS Template

The idea is to span your card to multiple columns to create a bento grid.

The same idea can be replicated to span the card in multiple rows with row-span- classes.

Example

We have create a few bento grids at aceternity where we talk about how you can create super simple and elegant looking bento grids with Tailwind CSS. Let's take a look at one such example.

The Dawn of Innovation
Explore the birth of groundbreaking ideas and inventions.
The Digital Revolution
Dive into the transformative power of technology.
The Art of Design
Discover the beauty of thoughtful and functional design.
The Power of Communication
Understand the impact of effective communication in our lives.
The Pursuit of Knowledge
Join the quest for understanding and enlightenment.
The Joy of Creation
Experience the thrill of bringing ideas to life.
The Spirit of Adventure
Embark on exciting journeys and thrilling discoveries.

Here, the card structure is similar to the one we have created above.

bento-grid-demo.tsx
import { cn } from "@/utils/cn";
import React from "react";
import { BentoGrid, BentoGridItem } from "../ui/bento-grid";
import {
  IconArrowWaveRightUp,
  IconBoxAlignRightFilled,
  IconBoxAlignTopLeft,
  IconClipboardCopy,
  IconFileBroken,
  IconSignature,
  IconTableColumn,
} from "@tabler/icons-react";
 
export default function BentoGridDemo() {
  return (
    <BentoGrid className="max-w-4xl mx-auto">
      {items.map((item, i) => (
        <BentoGridItem
          key={i}
          title={item.title}
          description={item.description}
          header={item.header}
          icon={item.icon}
          className={i === 3 || i === 6 ? "md:col-span-2" : ""}
        />
      ))}
    </BentoGrid>
  );
}
const Skeleton = () => (
  <div className="flex flex-1 w-full h-full min-h-[6rem] rounded-xl bg-gradient-to-br from-neutral-200 dark:from-neutral-900 dark:to-neutral-800 to-neutral-100"></div>
);
const items = [
  {
    title: "The Dawn of Innovation",
    description: "Explore the birth of groundbreaking ideas and inventions.",
    header: <Skeleton />,
    icon: <IconClipboardCopy className="h-4 w-4 text-neutral-500" />,
  },
  {
    title: "The Digital Revolution",
    description: "Dive into the transformative power of technology.",
    header: <Skeleton />,
    icon: <IconFileBroken className="h-4 w-4 text-neutral-500" />,
  },
  {
    title: "The Art of Design",
    description: "Discover the beauty of thoughtful and functional design.",
    header: <Skeleton />,
    icon: <IconSignature className="h-4 w-4 text-neutral-500" />,
  },
  {
    title: "The Power of Communication",
    description:
      "Understand the impact of effective communication in our lives.",
    header: <Skeleton />,
    icon: <IconTableColumn className="h-4 w-4 text-neutral-500" />,
  },
  {
    title: "The Pursuit of Knowledge",
    description: "Join the quest for understanding and enlightenment.",
    header: <Skeleton />,
    icon: <IconArrowWaveRightUp className="h-4 w-4 text-neutral-500" />,
  },
  {
    title: "The Joy of Creation",
    description: "Experience the thrill of bringing ideas to life.",
    header: <Skeleton />,
    icon: <IconBoxAlignTopLeft className="h-4 w-4 text-neutral-500" />,
  },
  {
    title: "The Spirit of Adventure",
    description: "Embark on exciting journeys and thrilling discoveries.",
    header: <Skeleton />,
    icon: <IconBoxAlignRightFilled className="h-4 w-4 text-neutral-500" />,
  },
];
bento-grid.tsx
import { cn } from "@/utils/cn";
 
export const BentoGrid = ({
  className,
  children,
}: {
  className?: string;
  children?: React.ReactNode;
}) => {
  return (
    <div
      className={cn(
        "grid md:auto-rows-[18rem] grid-cols-1 md:grid-cols-3 gap-4 max-w-7xl mx-auto ",
        className
      )}
    >
      {children}
    </div>
  );
};
 
export const BentoGridItem = ({
  className,
  title,
  description,
  header,
  icon,
}: {
  className?: string;
  title?: string | React.ReactNode;
  description?: string | React.ReactNode;
  header?: React.ReactNode;
  icon?: React.ReactNode;
}) => {
  return (
    <div
      className={cn(
        "row-span-1 rounded-xl group/bento hover:shadow-xl transition duration-200 shadow-input dark:shadow-none p-4 dark:bg-black dark:border-white/[0.2] bg-white border border-transparent justify-between flex flex-col space-y-4",
        className
      )}
    >
      {header}
      <div className="group-hover/bento:translate-x-2 transition duration-200">
        {icon}
        <div className="font-sans font-bold text-neutral-600 dark:text-neutral-200 mb-2 mt-2">
          {title}
        </div>
        <div className="font-sans font-normal text-neutral-600 text-xs dark:text-neutral-300">
          {description}
        </div>
      </div>
    </div>
  );
};

Notice how we are passing the md:col-span-2 class in the BentoGridItem component.

That makes sure that once the component renders on a medium screen, it will span 2 columns.

Conclusion

We've seen how we can make use of the col-span- and row-span- classes to create a bento grid.

We've also seen how we can make use of the grid-cols- and grid-rows- classes to create a bento grid with the number of rows and columns that we need inside of it.

If you liked this article, you can read more about how to create a bento grid with Tailwind CSS.

Cheers!

Want a custom website tailored to your needs? Let's talk

We have helped hundreds of founders with their website needs, we can help you too.

John Shahawy
Henrik Söderlund
John Ferry
Meru Gokhale
Georg Weingartner
Jonathan Barshop
Ray Thai
Tony Pujals
A product by Aceternity
Building in public at @mannupaaji