Skip to content

Radio

Basic Usage

The Radio field renders a group of radio buttons for selecting a single value.

Example
Digest cadence
php
Radio::make('digest_cadence')->options([
    'daily' => 'Daily',
    'weekly' => 'Weekly',
    'monthly' => 'Monthly',
])->default('weekly');

The default is null until a model or default() supplies a selection. Option values may be strings, numbers, booleans, or null; selected values are compared with strict equality.

Validation

With options, Radio automatically adds Rule::in(...) using the normalized option values. required() adds Laravel's required rule. It also exposes aria-required on the radiogroup and places the native required attribute on the first enabled option, which gives the browser one valid representative for the group without making a disabled option responsible for validation.

Options with Descriptions and Icons

Each option may include a description and resolver-backed icon name:

Example
Workflow status
php
Radio::make('workflow_status')->options([
    [
        'value' => 'draft',
        'label' => 'Draft',
        'description' => 'Keep changes internal until they are ready.',
        'icon' => 'SquarePenIcon',
    ],
    [
        'value' => 'review',
        'label' => 'In review',
        'description' => 'Ask reviewers to approve the next update.',
        'icon' => 'ClipboardCheckIcon',
    ],
    [
        'value' => 'archived',
        'label' => 'Archived',
        'description' => 'Locked for historical reference.',
        'icon' => 'ArchiveIcon',
        'disabled' => true,
    ],
])->cards()->columns(2)->default('review');

For options from arrays, Collections, models, or queries, choose the secondary line with optionDescription():

php
Radio::make('plan_id')
    ->options(Plan::query()->orderBy('name'), 'name', 'id')
    ->optionDescription('tagline');

Use mapDescriptionAs() when the text needs custom PHP logic:

php
Radio::make('plan_id')
    ->options(Plan::query()->orderBy('name'), 'name', 'id')
    ->mapDescriptionAs(fn (Plan $plan) => "{$plan->seats} seats included");

For simple translations, the shared option mappers also accept arrays:

php
Radio::make('plan')
    ->options(Plan::class, 'tier', 'id', 'billing_period')
    ->mapAs([
        'starter' => 'Starter',
        'team' => 'Team',
    ])
    ->mapDescriptionAs([
        'monthly' => 'Billed monthly',
        'yearly' => 'Billed yearly',
    ]);

Option icons are resolver-backed icon names. See Icons.

Radio intentionally supports a narrower option metadata contract than custom Combobox: description, icon, and disabled. Use Combobox when options need images, avatars, disabled reasons, selected suffixes, server-side search, or custom Vue option slots.

Option Sources and Mapping

options() accepts an array, Collection, Arrayable object, Eloquent model class, Eloquent or query builder, or a closure that returns one of those sources. Finite model and query sources are resolved on the server before the field is serialized.

Associative scalar arrays use each array key as the value and each item as the label. Object and array records use value and label by default. Pass label, value, and description keys as the second through fourth options() arguments, or configure them separately:

php
Radio::make('plan_id')
    ->options(Plan::query()->orderBy('name'))
    ->optionLabel('name')
    ->optionValue('id')
    ->optionDescription('tagline');

Each key selector also accepts a closure. Use mapAs(), mapValueAs(), and mapDescriptionAs() when a resolved label, value, or description needs a lookup map or custom closure. Mapping arrays keep the original resolved value when no map entry exists.

Variants

The radio field supports five visual variants:

VariantDescription
defaultStandard radio buttons with labels
segmentedCompact horizontal toggle (like iOS segmented control)
cardsBordered card boxes
pillsPill/tag style
buttonsButton toolbar style

You may compare the variants with the same options:

Example
Default
Segmented
Cards
Pills
Buttons
php
Radio::make('view')->options($views)->variant('default');
Radio::make('view')->options($views)->variant('segmented');
Radio::make('view')->options($views)->variant('cards');
Radio::make('view')->options($views)->variant('pills');
Radio::make('view')->options($views)->variant('buttons');

Shortcut methods:

php
Radio::make('view')->options($views)->segmented();
Radio::make('view')->options($views)->cards();
Radio::make('view')->options($views)->pills();
Radio::make('view')->options($views)->buttons();

The buttons() variant submits one radio value, validates against the configured options, and uses radio semantics.

Any other serialized variant falls back to default rendering in Vue and React. This keeps older or custom field data usable without creating an undocumented sixth visual style.

Inline

Display the radio buttons horizontally:

Example
Priority
php
Radio::make('priority')->options([
    'low' => 'Low',
    'medium' => 'Medium',
    'high' => 'High',
])->inline();

Columns

Display the options in a multi-column grid. The value is the maximum column target, and the grid automatically drops to fewer columns when the container is too narrow. Pass 1 or higher; lower values throw an InvalidArgumentException.

Example
Category
php
Radio::make('category')
    ->options($categories)
    ->columns(2);

Hide Indicator

Hide the radio dot indicator, which is useful for the cards variant:

Example
Theme
php
Radio::make('theme')
    ->options($themes)
    ->cards()
    ->hideIndicator();

Or use the explicit method:

php
Radio::make('theme')
    ->options($themes)
    ->showIndicator(false);

Size

Set the size of the radio buttons. Available sizes are sm and md (default).

Example
Compact priority
php
Radio::make('priority')->options($priorities)->size('sm');
Radio::make('priority')->options($priorities)->sm();

The serialized PHP default is null, which renders at medium size. Any value other than sm currently uses the medium styles; use sm() or leave the size unset instead of relying on custom size strings.

Using the Component Directly

Radio is exported from each package's components subpath. You may use it for standalone choices, or pass serialized props while rendering forms manually.

vue
<script setup lang="ts">
import { ref } from 'vue'
import { Building2, Rocket } from '@lucide/vue'
import { Radio } from '@inertiaui/form-vue/components'
import type { IconResolverFn } from '@inertiaui/form-vue'

const plan = ref('team')
const icons = { Building2Icon: Building2, RocketIcon: Rocket }
const iconResolver: IconResolverFn = (icon) => icons[icon as keyof typeof icons] ?? null

const plans = [
    { value: 'starter', label: 'Starter', description: 'For small teams.' },
    { value: 'team', label: 'Team', description: 'Most popular.', icon: 'RocketIcon' },
    {
        value: 'enterprise',
        label: 'Enterprise',
        description: 'Requires approval.',
        icon: 'Building2Icon',
        disabled: true,
    },
]
</script>

<template>
    <Radio
        v-model="plan"
        id="plan"
        name="plan"
        label="Plan"
        help="Choose the workspace billing plan."
        :options="plans"
        variant="cards"
        :columns="2"
        :show-indicator="false"
        size="sm"
        required
        :icon-resolver="iconResolver"
        data-testid="plan-radio"
    />
</template>
tsx
import { useState } from 'react'
import { Building2Icon, RocketIcon } from 'lucide-react'
import type { RadioOption } from '@inertiaui/form-react'
import { Radio } from '@inertiaui/form-react/components'
import type { IconResolverFn } from '@inertiaui/form-react'

const icons = { Building2Icon, RocketIcon }
const iconResolver: IconResolverFn = (icon) => icons[icon as keyof typeof icons] ?? null

const plans: RadioOption[] = [
    { value: 'starter', label: 'Starter', description: 'For small teams.' },
    { value: 'team', label: 'Team', description: 'Most popular.', icon: 'RocketIcon' },
    {
        value: 'enterprise',
        label: 'Enterprise',
        description: 'Requires approval.',
        icon: 'Building2Icon',
        disabled: true,
    },
]

export function Plan() {
    const [plan, setPlan] = useState<RadioOption['value']>('team')

    return (
        <Radio
            value={plan}
            onValueChange={setPlan}
            id="plan"
            name="plan"
            label="Plan"
            help="Choose the workspace billing plan."
            options={plans}
            variant="cards"
            columns={2}
            showIndicator={false}
            size="sm"
            required
            iconResolver={iconResolver}
            data-testid="plan-radio"
        />
    )
}

Native attributes and listeners target the underlying radiogroup element. See Native Attributes And Events.

Shared field methods remain available. You may use Validation, Conditional Visibility, Icons, and Styling for rules, visibility, icon resolvers, labels, help, layout, and class targets.