Components

Bento Grid

A container component that establishes a CSS Grid for creating "bento box" layouts.

Welcome back!

Here's a look at your project.

Go to dashboard

New Project

Invite Team

Collaboration

Work with your team in real-time.

Recent Activity

Alex pushed a new commit.
Lana merged a pull request.
New issue reported by a user.

Integrations

Connect your favorite tools.

Current Sprint

Help & Support

Databases

Databases

Security

Performance

Cloud Sync

Team SSO

Analytics

API Access

Reporting

Databases

1
2
3
4
5
6
7
8
9
10
11
12
13

Installation

CLI

Installation via the CLI is coming soon. For now, please follow the manual installation instructions below.

Manual

Install the necessary dependencies:

npm install tailwind-merge
yarn add tailwind-merge
pnpm add tailwind-merge

Copy and paste the following code into your project:

import React from 'react';
import { twMerge } from 'tailwind-merge';

type BentoGridProps = {
  /** The child `BentoItem` components to be rendered within the grid. */
  children: React.ReactNode;
  /** Optional classes to apply to the grid container for custom styling. */
  className?: string;
  /** The total number of columns in the grid. */
  columns?: number;
  /** The total number of rows in the grid. If provided, the grid will have a fixed height. */
  rows?: number;
};

/**
 * A container component that establishes a CSS Grid for creating "bento box" layouts.
 * It dynamically sets the grid structure based on the provided `columns` and optional `rows` props.
 */
export const BentoGrid = ({
  children,
  className,
  columns = 12,
  rows,
}: BentoGridProps) => {
  const gridStyle = {
    '--bento-grid-cols': columns.toString(),
    ...(rows && { '--bento-grid-rows': rows.toString() }),
  } as React.CSSProperties;

  return (
    <div
      style={gridStyle}
      className={twMerge(
        'grid w-full gap-4',
        'grid-cols-[repeat(var(--bento-grid-cols),_minmax(0,_1fr))]',
        // DECISION: Allow the grid to be either explicit or implicit.
        // If `rows` is provided, we create a grid with a fixed number of rows of equal height.
        // If not, we fall back to `auto-rows`, allowing the grid to grow vertically as needed.
        rows
          ? 'grid-rows-[repeat(var(--bento-grid-rows),_minmax(0,_1fr))]'
          : 'auto-rows-[8rem]',
        className
      )}
    >
      {children}
    </div>
  );
};

type BentoItemProps = {
  /** The content to be displayed inside the bento item. */
  children: React.ReactNode;
  /** Optional classes to apply to the item for custom styling. */
  className?: string;
  /** The number of columns the item should span. */
  colSpan?: number;
  /** The number of rows the item should span. */
  rowSpan?: number;
  /** The column line where the item should begin. */
  colStart?: number;
  /** The row line where the item should begin. */
  rowStart?: number;
};

/**
 * A component representing a single item or "cell" within a `BentoGrid`.
 * Its position and size are controlled via span and start props.
 */
export const BentoItem = ({
  children,
  className,
  colSpan = 1,
  rowSpan = 1,
  colStart,
  rowStart,
}: BentoItemProps) => {
  const itemStyle = {
    '--bento-col-span': colSpan.toString(),
    '--bento-row-span': rowSpan.toString(),
    ...(colStart && { '--bento-col-start': colStart.toString() }),
    ...(rowStart && { '--bento-row-start': rowStart.toString() }),
  } as React.CSSProperties;

  return (
    <div
      style={itemStyle}
      className={twMerge(
        'bg-[#1C1C1C] rounded-2xl shadow-sm border border-zinc-800 p-6',
        'col-[span_var(--bento-col-span)_/_span_var(--bento-col-span)]',
        'row-[span_var(--bento-row-span)_/_span_var(--bento-row-span)]',
        colStart && 'col-start-[var(--bento-col-start)]',
        rowStart && 'row-start-[var(--bento-row-start)]',
        'flex flex-col',
        className
      )}
    >
      {children}
    </div>
  );
};

Usage

Import BentoGrid and BentoItem to create a dynamic, non-uniform grid layout.

import { BentoGrid, BentoItem } from '@/components/ui/bento-grid';

export default function BentoGridDemo() {
  return (
    <BentoGrid columns={8} className='max-w-4xl mx-auto'>
      <BentoItem colSpan={5} rowSpan={2}>
        <div className='flex flex-col h-full p-2'>
          <h3 className='font-bold text-lg'>Main Feature</h3>
          <p className='text-sm text-zinc-400'>
            This item takes up more space.
          </p>
        </div>
      </BentoItem>
      <BentoItem colSpan={3}>
        <div className='flex flex-col h-full p-2'>
          <h3 className='font-bold'>Sidebar Item</h3>
        </div>
      </BentoItem>
      <BentoItem colSpan={3} rowSpan={2}>
        <div className='flex flex-col h-full p-2'>
          <h3 className='font-bold text-lg'>Tall Item</h3>
          <p className='text-sm text-zinc-400'>Spans multiple rows.</p>
        </div>
      </BentoItem>
      <BentoItem colSpan={5}>
        <div className='flex flex-col h-full p-2'>
          <h3 className='font-bold'>Footer Item</h3>
        </div>
      </BentoItem>
    </BentoGrid>
  );
}

Props

BentoGrid

PropTypeDefaultDescription
childrenReact.ReactNodeRequiredThe child BentoItem components to be rendered within the grid.
classNamestring-Optional classes to apply to the grid container for custom styling.
columnsnumber12The total number of columns in the grid.
rowsnumber-The total number of rows in the grid. If provided, the grid will have a fixed height.

BentoItem

PropTypeDefaultDescription
childrenReact.ReactNodeRequiredThe content to be displayed inside the bento item.
classNamestring-Optional classes to apply to the item for custom styling.
colSpannumber1The number of columns the item should span.
rowSpannumber1The number of rows the item should span.
colStartnumber-The column line where the item should begin.
rowStartnumber-The row line where the item should begin.