SubscriptionPlanPicker

Card-grid input for selecting a subscription plan. One card per plan; clicking writes the plan id to the form field. Highlights the current plan and (optionally) a recommended one.

Installation

pnpm dlx shadcn@latest add @shadmin/subscription-plan-picker

Usage

import { SubscriptionPlanPicker } from "@/components/admin";
 
const PLANS = [
  {
    id: "free",
    name: "Free",
    price: 0,
    currency: "USD",
    interval: "month",
    features: ["1 project", "Community support"],
  },
  {
    id: "pro",
    name: "Pro",
    price: 29,
    currency: "USD",
    interval: "month",
    features: ["Unlimited projects", "Email support", "10 GB storage"],
  },
];
 
<SubscriptionPlanPicker
  source="planId"
  plans={PLANS}
  recommendedPlanId="pro"
/>;

Wrap the picker in a <SimpleForm> (or any react-hook-form <Form> context). The selected plan id is written to the form value at source.

Props

PropRequiredTypeDefaultDescription
sourceRequiredstring-Form field receiving the plan id
plansRequiredreadonly SubscriptionPlan[]-Plan options
recommendedPlanIdOptionalstring-Highlights one card with a "Recommended" tag
disabledOptionalbooleanfalseDisable every card's select button
labelOptionalstring | falseInferredCustom label; pass false to hide
helperTextOptionalReactNode-Helper text rendered under the picker
classNameOptionalstring-CSS class applied to the wrapping FormField

Single-select behavior

The picker is a <radiogroup> semantically. Only one plan is selected at a time; clicking another card replaces the selection. The currently selected card is rendered with a primary border and a "Current" button label; the recommended card (when set, and not currently selected) gets a softer accent.

recommendedPlanId

<SubscriptionPlanPicker source="planId" plans={PLANS} recommendedPlanId="pro" />

Only one card is flagged as "Recommended" at a time. When the recommended plan is also the currently selected one, the "selected" styling wins.

SubscriptionPlan shape

See <SubscriptionPlanField>. The features field is rendered as a bullet list inside each card.