# Comparison Table

> A three-plan feature comparison table with a sticky first column, highlighted recommended plan and inner horizontal scroll on mobile.

- Author: Atelier UI
- License: MIT
- Page: https://chromotion.dev/en/components/comparison-table
- Site: Atelier UI (https://chromotion.dev)

## Install

```bash
npx shadcn@latest add https://chromotion.dev/r/comparison-table.json
```

Stack: React + TypeScript + Tailwind CSS v4, shadcn/ui conventions. `cn` comes from `@/lib/utils`.

## Dependencies

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

## Usage

```tsx title="components/comparison-table-demo.tsx"
import { ComparisonTable } from "@/components/comparison-table"

export default function ComparisonTableDemo() {
  return (
    <div className="w-full min-w-0 max-w-3xl">
      <ComparisonTable
        plans={[
          { name: "Starter", price: "$0 / mo" },
          { name: "Pro", price: "$24 / mo", recommended: true },
          { name: "Enterprise", price: "Custom" },
        ]}
        rows={[
          { feature: "Projects", values: ["3", "Unlimited", "Unlimited"] },
          { feature: "Team members", values: ["1", "10", "Unlimited"] },
          { feature: "Custom domains", values: [false, true, true] },
          { feature: "Version history", values: ["7 days", "90 days", "Forever"] },
          { feature: "Priority support", values: [false, true, true] },
          { feature: "Single sign-on (SSO)", values: [false, false, true] },
          { feature: "Audit logs", values: [false, false, true] },
        ]}
      />
    </div>
  )
}
```

## Source

```tsx title="components/comparison-table.tsx"
import * as React from "react"
import { Check, Minus } from "lucide-react"

import { cn } from "@/lib/utils"

export interface ComparisonPlan {
  name: string
  price?: string
  recommended?: boolean
}

export interface ComparisonRow {
  feature: string
  /** One entry per plan: true / false renders an icon, a string renders as text */
  values: (boolean | string)[]
}

export interface ComparisonTableProps extends React.ComponentProps<"div"> {
  plans: ComparisonPlan[]
  rows: ComparisonRow[]
}

export function ComparisonTable({ plans, rows, className, ...props }: ComparisonTableProps) {
  return (
    <div className={cn("relative w-full overflow-x-auto rounded-2xl border bg-card text-card-foreground", className)} {...props}>
      <table className="w-full min-w-136 border-separate border-spacing-0 text-sm">
        <caption className="sr-only">Plan comparison</caption>
        <thead>
          <tr>
            <th
              scope="col"
              className="sticky left-0 z-10 border-b bg-card px-4 py-5 text-left align-bottom text-xs font-medium uppercase tracking-wide text-muted-foreground"
            >
              Features
            </th>
            {plans.map((plan) => (
              <th
                key={plan.name}
                scope="col"
                className={cn("border-b px-4 py-5 text-center align-bottom", plan.recommended && "bg-primary/5")}
              >
                {plan.recommended && (
                  <span className="mb-2 inline-block rounded-full bg-primary px-2 py-0.5 text-[11px] font-semibold text-primary-foreground">
                    Recommended
                  </span>
                )}
                <span className="block text-base font-semibold">{plan.name}</span>
                {plan.price && <span className="block text-sm font-normal text-muted-foreground">{plan.price}</span>}
              </th>
            ))}
          </tr>
        </thead>
        <tbody>
          {rows.map((row, r) => {
            const last = r === rows.length - 1
            return (
              <tr key={row.feature}>
                <th
                  scope="row"
                  className={cn(
                    "sticky left-0 z-10 bg-card px-4 py-3.5 text-left font-medium",
                    !last && "border-b"
                  )}
                >
                  {row.feature}
                </th>
                {plans.map((plan, c) => {
                  const value = row.values[c]
                  return (
                    <td
                      key={plan.name}
                      className={cn("px-4 py-3.5 text-center", !last && "border-b", plan.recommended && "bg-primary/5")}
                    >
                      {value === true ? (
                        <>
                          <Check className="mx-auto size-4 text-primary" aria-hidden />
                          <span className="sr-only">Included</span>
                        </>
                      ) : value === false || value === undefined ? (
                        <>
                          <Minus className="mx-auto size-4 text-muted-foreground/60" aria-hidden />
                          <span className="sr-only">Not included</span>
                        </>
                      ) : (
                        <span className="text-muted-foreground">{value}</span>
                      )}
                    </td>
                  )
                })}
              </tr>
            )
          })}
        </tbody>
      </table>
    </div>
  )
}
```
