S
Special Field

Button Card

Selection using rich card components with icons, titles, and descriptions.

stable
card selection rich visual

Interactive demo of button card field

Choose the plan that best fits your needs

/**
 * Button Card Field Demo - Interactive examples of button card fields
 */

import { Form, FormBuilder } from '@saastro/forms';

import { FormProvider } from '@/components/FormProvider';
import { TooltipProvider } from '@/components/ui/tooltip';

const config = FormBuilder.create('button-card-demo')
  .layout('manual')
  .columns(12)
  .addField('plan', (f) =>
    f
      .type('button-card')
      .label('Select a Plan')
      .helperText('Choose the plan that best fits your needs')
      .buttonCardOptions([
        {
          value: 'free',
          title: 'Free',
          description: 'Perfect for getting started with basic features',
          icon: '🆓',
        },
        {
          value: 'pro',
          title: 'Pro',
          description: 'For growing teams who need more power',
          icon: '⭐',
          badge: 'Popular',
        },
        {
          value: 'enterprise',
          title: 'Enterprise',
          description: 'Custom solutions for large organizations',
          icon: '🏢',
        },
      ])
      .required('Please select a plan')
      .columns({ default: 12 }),
  )
  .addField('storage', (f) =>
    f
      .type('button-card')
      .label('Storage Option')
      .buttonCardOptions([
        {
          value: '10gb',
          title: '10 GB',
          description: 'Basic storage',
          icon: '💾',
        },
        {
          value: '100gb',
          title: '100 GB',
          description: 'Standard storage',
          icon: '💿',
        },
        {
          value: 'unlimited',
          title: 'Unlimited',
          description: 'No limits',
          icon: '☁️',
          badge: 'Best Value',
        },
      ])
      .required()
      .columns({ default: 12 }),
  )
  .addStep('main', ['plan', 'storage'])
  .build();

export default function ButtonCardDemo() {
  const handleSubmit = (data: Record<string, unknown>) => {
    console.log('Form submitted:', data);
    alert('Form submitted! Check console for data.');
  };

  return (
    <TooltipProvider>
      <FormProvider>
        <Form config={config} onSubmit={handleSubmit} className="space-y-6" />
      </FormProvider>
    </TooltipProvider>
  );
}

Overview

The button card field presents options as rich, card-style components. Each option can include an icon, title, description, and optional badge. Ideal for plan selection, feature comparison, or any choice where visual hierarchy helps users decide.

Usage

Basic Button Card

import { FormBuilder } from '@saastro/forms';

const config = FormBuilder.create('form')
  .addField('plan', (f) =>
    f
      .type('button-card')
      .label('Select a Plan')
      .buttonCardOptions([
        {
          value: 'free',
          title: 'Free',
          description: 'Perfect for getting started',
          icon: '🆓',
        },
        {
          value: 'pro',
          title: 'Pro',
          description: 'For growing teams',
          icon: '⭐',
          badge: 'Popular',
        },
        {
          value: 'enterprise',
          title: 'Enterprise',
          description: 'Custom solutions',
          icon: '🏢',
        },
      ])
      .required('Please select a plan'),
  )
  .addStep('main', ['plan'])
  .build();

Multi-select Cards

.addField('features', (f) =>
  f.type('button-card')
    .label('Select Features')
    .buttonCardOptions([
      { value: 'analytics', title: 'Analytics', description: 'Track metrics', icon: '📊' },
      { value: 'api', title: 'API Access', description: 'REST & GraphQL', icon: '🔌' },
      { value: 'support', title: 'Priority Support', description: '24/7 help', icon: '💬' },
    ])
    // Enable multi-select mode
    .value([])
    .optional()
)

JSON Configuration

{
  "type": "button-card",
  "label": "Select a Plan",
  "options": [
    {
      "value": "free",
      "title": "Free",
      "description": "Perfect for getting started",
      "icon": "🆓"
    },
    {
      "value": "pro",
      "title": "Pro",
      "description": "For growing teams",
      "icon": "⭐",
      "badge": "Popular"
    }
  ],
  "schema": { "required": true }
}

Props

PropTypeDefaultDescription
type'button-card'-Field type (required)
labelstring-Group label
helperTextstring-Help text below field
optionsButtonCardOption[]-Array of card options
valuestring | string[]-Selected value(s)
optionsClassNamestring-CSS classes for cards container grid
columnsPartial<Record<Breakpoint, number>>-Grid columns by breakpoint
disabledboolean | function | ConditionGroupfalseDisable all cards
hiddenboolean | function | ConditionGroup | ResponsivefalseHide the field

ButtonCardOption Object

PropertyTypeDescription
valuestringSelection value
titlestringCard title
descriptionstringCard description
iconstring | ReactNodeIcon (emoji or component)
badgestringOptional badge text
disabledbooleanDisable this card

Validation

Required Selection

.addField('tier', (f) =>
  f.type('button-card')
    .label('Select Tier')
    .buttonCardOptions([...])
    .required('Please select a tier')
)

Styling

Custom Classes

.addField('plan', (f) =>
  f.type('button-card')
    .label('Select a Plan')
    .buttonCardOptions([...])
    .required()
    .classNames({
      wrapper: 'bg-muted/30 p-4 rounded-lg',
      label: 'text-lg font-semibold mb-3',
      error: 'text-destructive text-sm',
    })
)

Cards Layout with optionsClassName

Control card arrangement:

{
  "type": "button-card",
  "label": "Select Plan",
  "optionsClassName": "grid grid-cols-1 md:grid-cols-3 gap-4",
  "options": [...]
}

Responsive Layout

.addField('plan', (f) =>
  f.type('button-card')
    .label('Select a Plan')
    .buttonCardOptions([...])
    .columns({ default: 12, md: 8 })
)

JSON Styling

{
  "type": "button-card",
  "label": "Select Plan",
  "wrapper_className": "bg-muted/30 p-6 rounded-xl",
  "label_className": "text-xl font-bold",
  "optionsClassName": "grid grid-cols-1 md:grid-cols-3 gap-6",
  "columns": { "default": 12 }
}

Accessibility

  • Keyboard navigation between cards
  • ARIA roles for selectable cards
  • Focus visible states
  • Screen reader announcements