S
Selection Field

Radio Group

Single selection from a list of options using radio buttons.

stable
radio selection single options

Interactive demo of radio group field

Choose the plan that fits your needs

Select delivery speed

/**
 * Radio Field Demo - Interactive examples of radio group fields
 */

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

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

const config = FormBuilder.create('radio-demo')
  .layout('manual')
  .columns(12)
  .addField('plan', (f) =>
    f
      .type('radio')
      .label('Select a plan')
      .helperText('Choose the plan that fits your needs')
      .options([
        { label: 'Free', value: 'free' },
        { label: 'Pro', value: 'pro' },
        { label: 'Enterprise', value: 'enterprise' },
      ])
      .value('pro')
      .required('Please select a plan')
      .columns({ default: 12, md: 6 }),
  )
  .addField('frequency', (f) =>
    f
      .type('radio')
      .label('Billing frequency')
      .options([
        { label: 'Monthly', value: 'monthly' },
        { label: 'Yearly (save 20%)', value: 'yearly' },
      ])
      .required()
      .columns({ default: 12, md: 6 }),
  )
  .addField('shipping', (f) =>
    f
      .type('radio')
      .label('Shipping method')
      .helperText('Select delivery speed')
      .options([
        { label: 'Standard (5-7 days)', value: 'standard' },
        { label: 'Express (2-3 days)', value: 'express' },
        { label: 'Overnight', value: 'overnight' },
      ])
      .required()
      .columns({ default: 12 }),
  )
  .addStep('main', ['plan', 'frequency', 'shipping'])
  .build();

export default function RadioDemo() {
  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 radio group field allows users to select exactly one option from a list. Use it when users need to see all options at once and make a single selection.

Usage

Basic Radio Group

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

const config = FormBuilder.create('survey')
  .addField('satisfaction', (f) =>
    f
      .type('radio')
      .label('How satisfied are you?')
      .options([
        { label: 'Very satisfied', value: 'very-satisfied' },
        { label: 'Satisfied', value: 'satisfied' },
        { label: 'Neutral', value: 'neutral' },
        { label: 'Dissatisfied', value: 'dissatisfied' },
      ])
      .required('Please select an option'),
  )
  .addStep('main', ['satisfaction'])
  .build();

With Default Value

.addField('plan', (f) =>
  f.type('radio')
    .label('Select a plan')
    .options([
      { label: 'Free', value: 'free' },
      { label: 'Pro', value: 'pro' },
      { label: 'Enterprise', value: 'enterprise' },
    ])
    .value('pro')
    .required()
)

With Descriptions

.addField('shipping', (f) =>
  f.type('radio')
    .label('Shipping method')
    .options([
      { label: 'Standard', value: 'standard', description: '5-7 business days' },
      { label: 'Express', value: 'express', description: '2-3 business days' },
      { label: 'Overnight', value: 'overnight', description: 'Next business day' },
    ])
    .required()
)

JSON Configuration

{
  "type": "radio",
  "label": "Select a plan",
  "options": [
    { "label": "Free", "value": "free" },
    { "label": "Pro", "value": "pro" },
    { "label": "Enterprise", "value": "enterprise" }
  ],
  "value": "pro",
  "schema": { "required": true }
}

Props

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

Option Object

PropertyTypeDescription
labelstringDisplay text
valuestringValue when selected
descriptionstringOptional description
disabledbooleanDisable this option

Validation

Required Selection

.addField('gender', (f) =>
  f.type('radio')
    .label('Gender')
    .options([
      { label: 'Male', value: 'male' },
      { label: 'Female', value: 'female' },
      { label: 'Other', value: 'other' },
      { label: 'Prefer not to say', value: 'none' },
    ])
    .required('Please select an option')
)

Conditional Logic

Show options based on another field

.addField('paymentMethod', (f) =>
  f.type('radio')
    .label('Payment method')
    .options([
      { label: 'Credit Card', value: 'card' },
      { label: 'PayPal', value: 'paypal' },
      { label: 'Bank Transfer', value: 'bank' },
    ])
    .hidden((values) => values.total === 0)
)

Styling

Custom Classes

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

Options Layout

Control how options are arranged using optionsClassName:

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

Responsive Layout

.addField('plan', (f) =>
  f.type('radio')
    .label('Select a plan')
    .options([...])
    .columns({ default: 12, md: 6 })
)

JSON Styling

{
  "type": "radio",
  "label": "Plan",
  "wrapper_className": "bg-muted/30 p-4 rounded-lg",
  "label_className": "text-lg font-semibold",
  "optionsClassName": "space-y-3",
  "columns": { "default": 12, "md": 6 }
}

Radio vs Select

Use RadioUse Select
2-5 options6+ options
Options need visibilitySpace is limited
Options have descriptionsSimple list
Important decisionQuick selection

Accessibility

  • Uses Radix UI RadioGroup for accessibility
  • Supports keyboard navigation (Arrow keys)
  • Proper ARIA roles and labels
  • Focus visible states
  • Screen reader announcements