Selection Field

Radio Group

Single selection from a list of options using radio buttons.

View as Markdown
stable
radioselectionsingleoptions

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

Prop Type Default Description
type 'radio' - Field type (required)
label string - Group label
options Option[] - Array of options
value string - Default selected value
optionsClassName string - CSS classes for options container
columns Partial<Record<Breakpoint, number>> - Grid columns by breakpoint
disabled boolean | function | ConditionGroup false Disable all options
hidden boolean | function | ConditionGroup | Responsive false Hide the field

Option Object

Property Type Description
label string Display text
value string Value when selected
description string Optional description
disabled boolean Disable 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 Radio Use Select
2-5 options 6+ options
Options need visibility Space is limited
Options have descriptions Simple list
Important decision Quick selection

Accessibility

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