S
Selection Field

Button Radio

Radio group styled as segmented buttons for visual selection.

stable
radio button segmented toggle

Interactive demo of button radio field

Select your preferred size

/**
 * Button Radio Field Demo - Interactive examples of button radio fields
 */

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

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

const config = FormBuilder.create('button-radio-demo')
  .layout('manual')
  .columns(12)
  .addField('size', (f) =>
    f
      .type('button-radio')
      .label('Size')
      .helperText('Select your preferred size')
      .options([
        { label: 'XS', value: 'xs' },
        { label: 'S', value: 's' },
        { label: 'M', value: 'm' },
        { label: 'L', value: 'l' },
        { label: 'XL', value: 'xl' },
      ])
      .value('m')
      .required()
      .columns({ default: 12, md: 6 }),
  )
  .addField('billing', (f) =>
    f
      .type('button-radio')
      .label('Billing Cycle')
      .options([
        { label: 'Monthly', value: 'monthly' },
        { label: 'Yearly (Save 20%)', value: 'yearly' },
      ])
      .value('monthly')
      .required()
      .columns({ default: 12, md: 6 }),
  )
  .addField('priority', (f) =>
    f
      .type('button-radio')
      .label('Priority')
      .options([
        { label: 'Low', value: 'low' },
        { label: 'Medium', value: 'medium' },
        { label: 'High', value: 'high' },
        { label: 'Urgent', value: 'urgent' },
      ])
      .required('Please select a priority')
      .columns({ default: 12 }),
  )
  .addStep('main', ['size', 'billing', 'priority'])
  .build();

export default function ButtonRadioDemo() {
  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-4" />
      </FormProvider>
    </TooltipProvider>
  );
}

Overview

The button radio field presents radio options as a segmented button group. It’s ideal when you have 2-5 options and want a more compact, visually prominent selection UI.

Usage

Basic Button Radio

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

const config = FormBuilder.create('form')
  .addField('size', (f) =>
    f
      .type('button-radio')
      .label('Size')
      .options([
        { label: 'S', value: 'small' },
        { label: 'M', value: 'medium' },
        { label: 'L', value: 'large' },
        { label: 'XL', value: 'xlarge' },
      ])
      .value('medium')
      .required(),
  )
  .addStep('main', ['size'])
  .build();

Binary Choice

.addField('billingCycle', (f) =>
  f.type('button-radio')
    .label('Billing')
    .options([
      { label: 'Monthly', value: 'monthly' },
      { label: 'Yearly', value: 'yearly' },
    ])
    .value('monthly')
    .required()
)

With Icons (via label)

.addField('view', (f) =>
  f.type('button-radio')
    .label('View')
    .options([
      { label: '📋 List', value: 'list' },
      { label: '📊 Grid', value: 'grid' },
      { label: '📈 Chart', value: 'chart' },
    ])
    .value('list')
    .required()
)

JSON Configuration

{
  "type": "button-radio",
  "label": "Size",
  "options": [
    { "label": "S", "value": "small" },
    { "label": "M", "value": "medium" },
    { "label": "L", "value": "large" }
  ],
  "value": "medium",
  "schema": { "required": true }
}

Props

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

Button Radio vs Radio

Use Button RadioUse Radio
2-5 optionsAny number
Compact UIDetailed options
Visual selectionDescription needed
Horizontal layoutVertical layout

Validation

Required Selection

.addField('priority', (f) =>
  f.type('button-radio')
    .label('Priority')
    .options([
      { label: 'Low', value: 'low' },
      { label: 'Medium', value: 'medium' },
      { label: 'High', value: 'high' },
    ])
    .required('Please select a priority')
)

Styling

Custom Classes

.addField('size', (f) =>
  f.type('button-radio')
    .label('Size')
    .options([...])
    .required()
    .classNames({
      wrapper: 'bg-muted/30 p-4 rounded-lg',
      label: 'text-lg font-semibold mb-3',
      error: 'text-destructive text-sm',
    })
)

Options Layout with optionsClassName

Control button arrangement:

{
  "type": "button-radio",
  "label": "Size",
  "optionsClassName": "flex flex-wrap gap-2",
  "options": [...]
}

Responsive Layout

.addField('billing', (f) =>
  f.type('button-radio')
    .label('Billing')
    .options([...])
    .columns({ default: 12, md: 6 })
)

JSON Styling

{
  "type": "button-radio",
  "label": "Size",
  "wrapper_className": "bg-muted/30 p-4 rounded-lg",
  "label_className": "text-lg font-semibold",
  "optionsClassName": "inline-flex gap-1",
  "columns": { "default": 12, "md": 6 }
}

Accessibility

  • Uses Radix UI ToggleGroup (single mode)
  • Keyboard navigation (Arrow keys)
  • Proper ARIA roles
  • Focus visible states