Selection Field

Button Radio

Radio group styled as segmented buttons for visual selection.

View as Markdown
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