# Feature Grid

The FeatureGrid block arranges a set of features into a bento layout, letting each one declare how many columns it spans so the arrangement itself ranks them.

- **Category:** Block
- **Docs:** https://uibeats.com/docs/block/feature-grid
- **Registry:** https://uibeats.com/r/feature-grid.json
- **License:** MIT
- **npm dependencies:** `lucide-react`

## Installation

```bash
npx shadcn@latest add https://uibeats.com/r/feature-grid.json
```

Or install the dependencies and copy the source below:

```bash
npm install lucide-react
```

## When to use

For the section after the hero. The uneven spans are what rank your features, so give the one you lead with two columns and the rest one.

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `features` | `Feature[]` | `-` | The features to lay out, in display order (required) |
| `heading` | `string` | `-` | Section heading above the grid |
| `description` | `string` | `-` | Supporting line under the heading |
| `columns` | `number` | `3` | Columns from the sm breakpoint up. Below it every card is full width |
| `Feature: title` | `string` | `-` | The feature's name |
| `Feature: description` | `string` | `-` | One or two lines on what it does |
| `Feature: icon` | `LucideIcon` | `-` | Optional icon shown above the title |
| `Feature: colSpan` | `number` | `1` | Columns this feature spans, from sm up |
| `className` | `string` | `''` | Additional CSS classes for styling |

## Example usage

```tsx
"use client";
import { Boxes, Palette, Sparkles, Zap } from "lucide-react";
import { FeatureGrid } from "@/components/demo/block/feature-grid";

const FEATURES = [
  {
    title: "Copy, paste, own it",
    description:
      "Every component lands in your repo as plain source you can edit.",
    icon: Boxes,
    colSpan: 2,
  },
  {
    title: "Motion included",
    description:
      "Animation is part of the component, not a wrapper added later.",
    icon: Sparkles,
    colSpan: 1,
  },
  {
    title: "Themed by default",
    description: "Colours come from your own tokens.",
    icon: Palette,
    colSpan: 1,
  },
  {
    title: "Documented properly",
    description: "Every prop has a table, a playground, and a reason to exist.",
    icon: Zap,
    colSpan: 2,
  },
];

const FeatureGridUsage = () => {
  return (
    <FeatureGrid
      heading="Built to be edited"
      description="Four things that matter more than the component count."
      features={FEATURES}
    />
  );
};

export default FeatureGridUsage;
```

## Source

Save as `components/ui/feature-grid.tsx`.

```tsx
"use client";

import type { LucideIcon } from "lucide-react";
import { BentoGrid, BentoCard } from "@/components/demo/component/bento-grid";
import { cn } from "@/lib/utils";

export interface Feature {
  title: string;
  description: string;
  icon?: LucideIcon;
  /** Columns this feature spans, from the sm breakpoint up. */
  colSpan?: number;
}

interface FeatureGridProps {
  heading?: string;
  description?: string;
  features: Feature[];
  /** Columns from the sm breakpoint up. */
  columns?: number;
  className?: string;
}

/**
 * A feature section laid out as a bento grid.
 *
 * Each feature declares its own `colSpan`, so the arrangement ranks them: the
 * capability you lead with takes two columns and the supporting ones take one.
 * That only works if you mean the ranking. Give every feature the same span and
 * you have an ordinary grid with extra steps, and an even grid says it more
 * clearly.
 */
export function FeatureGrid({
  heading,
  description,
  features,
  columns = 3,
  className = "",
}: FeatureGridProps) {
  return (
    <section className={cn("bg-background px-6 py-24", className)}>
      {heading || description ? (
        <div className="mx-auto mb-14 max-w-2xl text-center">
          {heading ? (
            <h2 className="text-3xl font-bold tracking-tighter text-balance sm:text-4xl">
              {heading}
            </h2>
          ) : null}
          {description ? (
            <p className="mt-3 text-balance text-muted-foreground">
              {description}
            </p>
          ) : null}
        </div>
      ) : null}

      <BentoGrid columns={columns} gap={16} className="mx-auto max-w-5xl">
        {features.map(({ title, description: body, icon: Icon, colSpan }) => (
          <BentoCard key={title} colSpan={colSpan}>
            {Icon ? (
              <span className="mb-4 flex size-9 items-center justify-center rounded-lg bg-muted">
                <Icon className="size-4 text-primary" />
              </span>
            ) : null}
            <h3 className="text-sm font-semibold">{title}</h3>
            <p className="mt-1.5 text-sm text-muted-foreground">{body}</p>
          </BentoCard>
        ))}
      </BentoGrid>
    </section>
  );
}

export default FeatureGrid;
```

## Credits

Written by [Claude Code](https://claude.com/claude-code).
