---
title: RadioGroup
description: Single-select radio group with card, list, and table layout variants.
sidebar:
  order: 8
search:
  tags: [single selection, options, radio, choice]
---

# RadioGroup

> Single-select radio group with card, list, and table layout variants.

## Import

```tsx
import { RadioGroup } from 'moraine'
```

## Slot Structure

Radiogroup root with mutually exclusive radio items. Use `FormField` for the group label, description, help text, and validation message.

```text
root
└── item (×n)
    ├── input
    ├── control
    │   └── indicator
    └── wrapper (optional)
        ├── label (optional)
        └── description (optional)
```

## Examples

### Variants

List, card, and table variants for single selection.

```tsx
function Variants() {
  const ITEMS = [
    { value: 'starter', label: 'Starter', description: 'For personal projects' },
    { value: 'pro', label: 'Pro', description: 'For teams and scaling' },
    { value: 'enterprise', label: 'Enterprise', description: 'For regulated workloads' },
  ]

  return (
    <div class="gap-4 grid lg:grid-cols-3 sm:grid-cols-2">
      <div class="p-4 b-(1 border) rounded-lg">
        <FormField label="List">
          <RadioGroup items={ITEMS} defaultValue="starter" />
        </FormField>
      </div>
      <div class="p-4 b-(1 border) rounded-lg">
        <FormField label="Card">
          <RadioGroup items={ITEMS} variant="card" defaultValue="pro" />
        </FormField>
      </div>
      <div class="p-4 b-(1 border) rounded-lg">
        <FormField label="Table">
          <RadioGroup
            items={ITEMS}
            variant="table"
            orientation="vertical"
            defaultValue="enterprise"
          />
        </FormField>
      </div>
    </div>
  )
}
```

### Sizes + Orientation

Size scale and vertical/horizontal modes.

```tsx
function SizesOrientation() {
  const ITEMS = [
    { value: 'starter', label: 'Starter', description: 'For personal projects' },
    { value: 'pro', label: 'Pro', description: 'For teams and scaling' },
    { value: 'enterprise', label: 'Enterprise', description: 'For regulated workloads' },
  ]

  const SIZES: RadioGroupSizeName[] = ['xs', 'sm', 'md', 'lg', 'xl']

  type RadioGroupSizeName = Exclude<RadioGroupT.Variant['size'], undefined>

  return (
    <div class="gap-4 grid lg:grid-cols-2">
      <div class="space-y-3">
        <For each={SIZES}>
          {(size) => (
            <div class="p-4 b-(1 border) rounded-lg">
              <FormField label={`Size ${size}`}>
                <RadioGroup items={ITEMS} size={size} defaultValue="starter" />
              </FormField>
            </div>
          )}
        </For>
      </div>
      <div class="space-y-3">
        <div class="p-4 b-(1 border) rounded-lg">
          <FormField label="Horizontal card">
            <RadioGroup items={ITEMS} variant="card" orientation="horizontal" defaultValue="pro" />
          </FormField>
        </div>
        <div class="p-4 b-(1 border) rounded-lg">
          <FormField label="Horizontal table">
            <RadioGroup
              items={ITEMS}
              variant="table"
              orientation="horizontal"
              defaultValue="enterprise"
            />
          </FormField>
        </div>
      </div>
    </div>
  )
}
```

### Controlled + Disabled

Controlled value with disabled option in data set.

```tsx
function ControlledDisabled() {
  const ITEMS = [
    { value: 'starter', label: 'Starter', description: 'For personal projects' },
    { value: 'pro', label: 'Pro', description: 'For teams and scaling' },
    { value: 'enterprise', label: 'Enterprise', description: 'For regulated workloads' },
  ]

  const [value, setValue] = createSignal('pro')

  return (
    <div class="max-w-xl space-y-3">
      <FormField label="Plan selector">
        <RadioGroup
          items={[
            ...ITEMS,
            {
              value: 'legacy',
              label: 'Legacy',
              description: 'No longer available',
              disabled: true,
            },
          ]}
          value={value()}
          onChange={setValue}
          variant="table"
          orientation="horizontal"
        />
      </FormField>
      <p class="text-xs text-muted-foreground">Current plan: {value()}</p>
    </div>
  )
}
```

### Indicator Positions

Start/end/hidden indicator styles with card variant.

```tsx
function IndicatorPositions() {
  const ITEMS = [
    { value: 'starter', label: 'Starter', description: 'For personal projects' },
    { value: 'pro', label: 'Pro', description: 'For teams and scaling' },
    { value: 'enterprise', label: 'Enterprise', description: 'For regulated workloads' },
  ]

  const INDICATORS: RadioGroupIndicatorName[] = ['start', 'end', 'hidden']

  type RadioGroupIndicatorName = Exclude<RadioGroupT.Variant['indicator'], undefined>

  return (
    <div class="gap-4 grid lg:grid-cols-3 sm:grid-cols-2">
      <For each={INDICATORS}>
        {(indicator) => (
          <div class="p-4 b-(1 border) rounded-lg">
            <FormField label={`Indicator ${indicator}`}>
              <RadioGroup items={ITEMS} variant="card" indicator={indicator} defaultValue="pro" />
            </FormField>
          </div>
        )}
      </For>
    </div>
  )
}
```

## API Reference

### Attributes

#### `root`

Radio group container that owns selection state and layout.

##### ARIA Attributes

| Attribute | Type | Description |
| --- | --- | --- |
| aria-disabled | boolean \| string \| undefined | Indicates that the control is disabled. |
| aria-orientation | boolean \| string \| undefined | Communicates horizontal or vertical orientation. |
| aria-readonly | boolean \| string \| undefined | Indicates that the control value cannot be changed by the user. |
| aria-required | boolean \| string \| undefined | Indicates that user input is required. |
| role | string | Defines the semantic role exposed to assistive technology. |

#### `item`

Wrapper for one radio option.

##### Data Attributes

| Attribute | Type | Description |
| --- | --- | --- |
| data-checked | string \| undefined | Present when the item is checked or selected. |

#### `control`

Visible radio control for an individual option.

##### Data Attributes

| Attribute | Type | Description |
| --- | --- | --- |
| data-checked | string \| undefined | Present when the item is checked or selected. |
| data-disabled | string \| undefined | Present when the component or item is disabled. |
| data-invalid | string \| undefined | Present when the field has a validation error. |
| data-readonly | string \| undefined | Present when the field is read-only. |
| data-required | string \| undefined | Present when the field is required. |

#### `indicator`

Selected-state layer inside an option control.

##### Data Attributes

| Attribute | Type | Description |
| --- | --- | --- |
| data-checked | string \| undefined | Present when the item is checked or selected. |
| data-disabled | string \| undefined | Present when the component or item is disabled. |
| data-invalid | string \| undefined | Present when the field has a validation error. |
| data-readonly | string \| undefined | Present when the field is read-only. |
| data-required | string \| undefined | Present when the field is required. |

#### `wrapper`

Inner layout wrapper used by grouped radio variants.

#### `label`

Primary label text for an option.

#### `description`

Supporting description for an option.

### Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| class | ClassValue | — | Class applied to the component root or trigger element. |
| classes | RadioGroupT.Classes \| undefined | — | — |
| defaultValue | string \| undefined | — | The default value of the input (uncontrolled). |
| disabled | boolean \| undefined | false | Whether the input is disabled. |
| id | string \| undefined | — | The ID of the input element. |
| indicator | RadioGroupItemIndicator \| undefined | — | — |
| items | (string \| RadioGroupT.Item)[] \| undefined | — | Array of items to render in the group. |
| name | string \| undefined | — | The name of the input element, used for form submission. |
| onChange | ((value: string) => void) \| undefined | — | Callback when the selected value changes. |
| orientation | "horizontal" \| "vertical" \| undefined | vertical | The orientation of the radio group. |
| readOnly | boolean \| undefined | false | Whether the input is read-only. |
| ref | JSX.HTMLElementTags["div"] extends { ref?: infer Ref; } ? Ref : never \| undefined | — | — |
| required | boolean \| undefined | false | Whether the input is required. |
| size | "xs" \| "sm" \| "md" \| "lg" \| "xl" \| undefined | — | — |
| style | JSX.CSSProperties \| undefined | — | — |
| styles | RadioGroupT.Styles \| undefined | — | — |
| value | string \| undefined | — | The current value of the input (controlled). |
| variant | RadioGroupItemVariant \| undefined | — | — |

### Items

A radio item object.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| description | JSX.Element | — | Description for the radio item. |
| disabled | boolean \| undefined | — | Whether the item is disabled. |
| label | JSX.Element | — | Label for the radio item. |
| value | string \| undefined | — | Value of the radio item. |

### ARIA

Accessibility attributes and roles emitted by the component markup.

| Attribute | Type | Description |
| --- | --- | --- |
| aria-disabled | boolean \| string \| undefined | Indicates that the control is disabled. |
| aria-orientation | boolean \| string \| undefined | Communicates horizontal or vertical orientation. |
| aria-readonly | boolean \| string \| undefined | Indicates that the control value cannot be changed by the user. |
| aria-required | boolean \| string \| undefined | Indicates that user input is required. |
| role | string | Defines the semantic role exposed to assistive technology. |

### Data Attributes

State and slot attributes exposed for styling hooks and selectors.

| Attribute | Type | Description |
| --- | --- | --- |
| data-checked | string \| undefined | Present when the item is checked or selected. |
| data-disabled | string \| undefined | Present when the component or item is disabled. |
| data-invalid | string \| undefined | Present when the field has a validation error. |
| data-readonly | string \| undefined | Present when the field is read-only. |
| data-required | string \| undefined | Present when the field is required. |
| data-slot | string | Identifies the rendered slot for styling hooks and selectors. |
