# Plan Card

> Pricing tier for plan pages and upgrade dialogs.

Component · Commerce · Pro kit · [Live page](https://ui.twentyfirst.media/pro/components/PlanCard/)

Pricing tier for plan pages and upgrade dialogs. Wrap several in `.tf-plans` for an even grid.

Props: `name`, `text`, `price` (number, 0 = "Free", null = "Let’s talk"), `was` (struck price, e.g. monthly vs yearly), `per`, `features` (strings; prefix "-" for not included), `featured` (dark card + flag), `flag`, `current` (disables the button), `disabled` (disables the button, e.g. while a checkout starts or when the plan is not available), `cta`, `onPick`.

- One featured plan, the one most customers should pick. It inverts to the frame color.
- List the same features in the same order on every card, so rows can be compared; mark missing ones instead of leaving them out.
- Pair with a Segmented Monthly / Yearly switch that states the saving.

## Example

```tsx
import { PlanCard } from "@twentyfirstmedia/pro";

export default function Preview() {
  return (
    <div className="tf-plans">
      <PlanCard
        name="Starter"
        price={9}
        was={12}
        per="per seat / month, billed yearly"
        text="For small teams getting organised"
        features={[
          "3 projects",
          "1 agent · Haiku only",
          "Shop with up to 50 products",
          "-Approvals and audit log",
          "-SSO",
        ]}
      />
      <PlanCard
        name="Team"
        price={24}
        was={29}
        per="per seat / month, billed yearly"
        text="Everything a growing company runs on"
        featured
        features={[
          "Unlimited projects",
          "10 agents · all models",
          "Unlimited products, 3 channels",
          "Approvals and audit log",
          "-SSO",
        ]}
      />
      <PlanCard
        name="Business"
        price={null}
        text="Security, control and an SLA"
        cta="Talk to sales"
        current={false}
        features={["Everything in Team", "SSO and SCIM", "EU data residency", "99.9% uptime SLA"]}
      />
    </div>
  );
}
```

## Props

### PlanCard

| Prop | Type | Notes |
| --- | --- | --- |
| `name` | `string` | Required |
| `text?` | `string` |  |
| `price` | `number \| null \| ReactNode` | A number is formatted as money (0 = "Free", null = "Let's talk"); any other node is shown as it is, e.g. a placeholder ("—") or a price the app formatted itself. |
| `pricePrefix?` | `ReactNode` | Before the price, e.g. "from". |
| `priceNote?` | `ReactNode` | Under the price: net or gross, billed yearly, VAT note … |
| `was?` | `number` |  |
| `per?` | `string` |  |
| `features?` | `string[]` |  |
| `featured?` | `boolean` |  |
| `flag?` | `string` |  |
| `current?` | `boolean` |  |
| `cta?` | `string` |  |
| `disabled?` | `boolean` | Disable the button, e.g. while a checkout starts or when the plan cannot be picked. |
| `onPick?` | `() => void` |  |
| `href?` | `string` | The action as a link (checkout or sign-up page), with `linkAs` for a router's link. |
| `linkAs?` | `ElementType` |  |
| `className?` | `string` | Extra class on the root element. |
| `style?` | `CSSProperties` | Inline style on the root element. |
