---
title: Radio
description: Radio buttons for mutually exclusive choices.
icon: circle-dot
---

import { ComponentPreview } from "/snippets/component-preview.mdx";

Radio buttons let users select exactly one option from a set. Each `Radio` represents a single choice; wrap them in a `RadioGroup` to link them together and track the selected value.

## Basic Usage

A `RadioGroup` is a container that holds `Radio` children. Each radio needs an `option` (what gets stored when selected) and a `label` (what the user sees). The group tracks which radio is selected and stores its `option` string in state:

<ComponentPreview json={{"view":{"cssClass":"w-fit mx-auto","name":"radiogroup_1","type":"RadioGroup","children":[{"name":"radio_1","value":false,"type":"Radio","option":"sm","label":"Small","disabled":false,"required":false},{"name":"radio_2","value":false,"type":"Radio","option":"md","label":"Medium","disabled":false,"required":false},{"name":"radio_3","value":false,"type":"Radio","option":"lg","label":"Large","disabled":false,"required":false}]},"state":{"radio_1":false,"radio_2":false,"radio_3":false}}} playground="ZnJvbSBwcmVmYWJfdWkuY29tcG9uZW50cyBpbXBvcnQgUmFkaW8sIFJhZGlvR3JvdXAKCndpdGggUmFkaW9Hcm91cChjc3NfY2xhc3M9InctZml0IG14LWF1dG8iKToKICAgIFJhZGlvKG9wdGlvbj0ic20iLCBsYWJlbD0iU21hbGwiKQogICAgUmFkaW8ob3B0aW9uPSJtZCIsIGxhYmVsPSJNZWRpdW0iKQogICAgUmFkaW8ob3B0aW9uPSJsZyIsIGxhYmVsPSJMYXJnZSIpCg">
<CodeGroup>
```python Python icon="python"
from prefab_ui.components import Radio, RadioGroup

with RadioGroup(css_class="w-fit mx-auto"):
    Radio(option="sm", label="Small")
    Radio(option="md", label="Medium")
    Radio(option="lg", label="Large")
```
```json Protocol icon="brackets-curly"
{
  "view": {
    "cssClass": "w-fit mx-auto",
    "name": "radiogroup_1",
    "type": "RadioGroup",
    "children": [
      {
        "name": "radio_1",
        "value": false,
        "type": "Radio",
        "option": "sm",
        "label": "Small",
        "disabled": false,
        "required": false
      },
      {
        "name": "radio_2",
        "value": false,
        "type": "Radio",
        "option": "md",
        "label": "Medium",
        "disabled": false,
        "required": false
      },
      {
        "name": "radio_3",
        "value": false,
        "type": "Radio",
        "option": "lg",
        "label": "Large",
        "disabled": false,
        "required": false
      }
    ]
  },
  "state": {"radio_1": false, "radio_2": false, "radio_3": false}
}
```
</CodeGroup>
</ComponentPreview>

## Pre-selected Option

Set `value=True` on a radio to have it selected when the component first renders:

<ComponentPreview json={{"view":{"cssClass":"w-fit mx-auto","name":"radiogroup_2","type":"RadioGroup","children":[{"name":"radio_4","value":false,"type":"Radio","option":"sm","label":"Small","disabled":false,"required":false},{"name":"radio_5","value":true,"type":"Radio","option":"md","label":"Medium","disabled":false,"required":false},{"name":"radio_6","value":false,"type":"Radio","option":"lg","label":"Large","disabled":false,"required":false}]},"state":{"radio_4":false,"radio_5":true,"radio_6":false}}} playground="ZnJvbSBwcmVmYWJfdWkuY29tcG9uZW50cyBpbXBvcnQgUmFkaW8sIFJhZGlvR3JvdXAKCndpdGggUmFkaW9Hcm91cChjc3NfY2xhc3M9InctZml0IG14LWF1dG8iKToKICAgIFJhZGlvKG9wdGlvbj0ic20iLCBsYWJlbD0iU21hbGwiKQogICAgUmFkaW8ob3B0aW9uPSJtZCIsIGxhYmVsPSJNZWRpdW0iLCB2YWx1ZT1UcnVlKQogICAgUmFkaW8ob3B0aW9uPSJsZyIsIGxhYmVsPSJMYXJnZSIpCg">
<CodeGroup>
```python Python icon="python"
from prefab_ui.components import Radio, RadioGroup

with RadioGroup(css_class="w-fit mx-auto"):
    Radio(option="sm", label="Small")
    Radio(option="md", label="Medium", value=True)
    Radio(option="lg", label="Large")
```
```json Protocol icon="brackets-curly"
{
  "view": {
    "cssClass": "w-fit mx-auto",
    "name": "radiogroup_2",
    "type": "RadioGroup",
    "children": [
      {
        "name": "radio_4",
        "value": false,
        "type": "Radio",
        "option": "sm",
        "label": "Small",
        "disabled": false,
        "required": false
      },
      {
        "name": "radio_5",
        "value": true,
        "type": "Radio",
        "option": "md",
        "label": "Medium",
        "disabled": false,
        "required": false
      },
      {
        "name": "radio_6",
        "value": false,
        "type": "Radio",
        "option": "lg",
        "label": "Large",
        "disabled": false,
        "required": false
      }
    ]
  },
  "state": {"radio_4": false, "radio_5": true, "radio_6": false}
}
```
</CodeGroup>
</ComponentPreview>

## Disabled Options

Individual radios can be disabled while the rest of the group remains interactive. This is useful for options that exist but aren't currently available:

<ComponentPreview json={{"view":{"cssClass":"w-fit mx-auto","name":"radiogroup_3","type":"RadioGroup","children":[{"name":"radio_7","value":true,"type":"Radio","option":"free","label":"Free","disabled":false,"required":false},{"name":"radio_8","value":false,"type":"Radio","option":"pro","label":"Pro","disabled":false,"required":false},{"name":"radio_9","value":false,"type":"Radio","option":"enterprise","label":"Enterprise","disabled":true,"required":false}]},"state":{"radio_7":true,"radio_8":false,"radio_9":false}}} playground="ZnJvbSBwcmVmYWJfdWkuY29tcG9uZW50cyBpbXBvcnQgUmFkaW8sIFJhZGlvR3JvdXAKCndpdGggUmFkaW9Hcm91cChjc3NfY2xhc3M9InctZml0IG14LWF1dG8iKToKICAgIFJhZGlvKG9wdGlvbj0iZnJlZSIsIGxhYmVsPSJGcmVlIiwgdmFsdWU9VHJ1ZSkKICAgIFJhZGlvKG9wdGlvbj0icHJvIiwgbGFiZWw9IlBybyIpCiAgICBSYWRpbyhvcHRpb249ImVudGVycHJpc2UiLCBsYWJlbD0iRW50ZXJwcmlzZSIsIGRpc2FibGVkPVRydWUpCg">
<CodeGroup>
```python Python icon="python"
from prefab_ui.components import Radio, RadioGroup

with RadioGroup(css_class="w-fit mx-auto"):
    Radio(option="free", label="Free", value=True)
    Radio(option="pro", label="Pro")
    Radio(option="enterprise", label="Enterprise", disabled=True)
```
```json Protocol icon="brackets-curly"
{
  "view": {
    "cssClass": "w-fit mx-auto",
    "name": "radiogroup_3",
    "type": "RadioGroup",
    "children": [
      {
        "name": "radio_7",
        "value": true,
        "type": "Radio",
        "option": "free",
        "label": "Free",
        "disabled": false,
        "required": false
      },
      {
        "name": "radio_8",
        "value": false,
        "type": "Radio",
        "option": "pro",
        "label": "Pro",
        "disabled": false,
        "required": false
      },
      {
        "name": "radio_9",
        "value": false,
        "type": "Radio",
        "option": "enterprise",
        "label": "Enterprise",
        "disabled": true,
        "required": false
      }
    ]
  },
  "state": {"radio_7": true, "radio_8": false, "radio_9": false}
}
```
</CodeGroup>
</ComponentPreview>

## Reading the Value

Use `.rx` on the `RadioGroup` to get a reactive reference to the selected radio's `option` string. When the user clicks a different option, anything bound to `.rx` updates automatically:

<ComponentPreview json={{"view":{"cssClass":"gap-4 w-fit mx-auto","type":"Column","children":[{"name":"radiogroup_4","type":"RadioGroup","children":[{"name":"radio_10","value":false,"type":"Radio","option":"heart-of-gold","label":"Heart of Gold","disabled":false,"required":false},{"name":"radio_11","value":false,"type":"Radio","option":"bistromath","label":"Bistromath","disabled":false,"required":false},{"name":"radio_12","value":false,"type":"Radio","option":"vogon-cruiser","label":"Vogon Constructor Fleet","disabled":false,"required":false}]},{"content":"Selected ship: {{ radiogroup_4 }}","type":"Text"}]},"state":{"radio_10":false,"radio_11":false,"radio_12":false}}} playground="ZnJvbSBwcmVmYWJfdWkuY29tcG9uZW50cyBpbXBvcnQgQ29sdW1uLCBSYWRpbywgUmFkaW9Hcm91cCwgVGV4dAoKd2l0aCBDb2x1bW4oZ2FwPTQsIGNzc19jbGFzcz0idy1maXQgbXgtYXV0byIpOgogICAgd2l0aCBSYWRpb0dyb3VwKCkgYXMgZ3JvdXA6CiAgICAgICAgUmFkaW8ob3B0aW9uPSJoZWFydC1vZi1nb2xkIiwgbGFiZWw9IkhlYXJ0IG9mIEdvbGQiKQogICAgICAgIFJhZGlvKG9wdGlvbj0iYmlzdHJvbWF0aCIsIGxhYmVsPSJCaXN0cm9tYXRoIikKICAgICAgICBSYWRpbyhvcHRpb249InZvZ29uLWNydWlzZXIiLCBsYWJlbD0iVm9nb24gQ29uc3RydWN0b3IgRmxlZXQiKQogICAgVGV4dChmIlNlbGVjdGVkIHNoaXA6IHtncm91cC5yeH0iKQo">
<CodeGroup>
```python Python icon="python"
from prefab_ui.components import Column, Radio, RadioGroup, Text

with Column(gap=4, css_class="w-fit mx-auto"):
    with RadioGroup() as group:
        Radio(option="heart-of-gold", label="Heart of Gold")
        Radio(option="bistromath", label="Bistromath")
        Radio(option="vogon-cruiser", label="Vogon Constructor Fleet")
    Text(f"Selected ship: {group.rx}")
```
```json Protocol icon="brackets-curly"
{
  "view": {
    "cssClass": "gap-4 w-fit mx-auto",
    "type": "Column",
    "children": [
      {
        "name": "radiogroup_4",
        "type": "RadioGroup",
        "children": [
          {
            "name": "radio_10",
            "value": false,
            "type": "Radio",
            "option": "heart-of-gold",
            "label": "Heart of Gold",
            "disabled": false,
            "required": false
          },
          {
            "name": "radio_11",
            "value": false,
            "type": "Radio",
            "option": "bistromath",
            "label": "Bistromath",
            "disabled": false,
            "required": false
          },
          {
            "name": "radio_12",
            "value": false,
            "type": "Radio",
            "option": "vogon-cruiser",
            "label": "Vogon Constructor Fleet",
            "disabled": false,
            "required": false
          }
        ]
      },
      {"content": "Selected ship: {{ radiogroup_4 }}", "type": "Text"}
    ]
  },
  "state": {"radio_10": false, "radio_11": false, "radio_12": false}
}
```
</CodeGroup>
</ComponentPreview>

Click a radio and the text updates instantly. The `.rx` reference holds the `option` string — `"heart-of-gold"`, `"bistromath"`, etc. — not the label.

## Actions

The `on_change` parameter on `RadioGroup` fires actions when the selection changes. Inside the action, `$event` holds the newly selected `option` string:

<ComponentPreview json={{"view":{"cssClass":"gap-4 w-fit mx-auto","type":"Column","children":[{"name":"radiogroup_5","type":"RadioGroup","onChange":{"action":"setState","key":"choice","value":"You picked: {{ $event }}"},"children":[{"name":"radio_13","value":false,"type":"Radio","option":"light","label":"Light","disabled":false,"required":false},{"name":"radio_14","value":false,"type":"Radio","option":"dark","label":"Dark","disabled":false,"required":false},{"name":"radio_15","value":true,"type":"Radio","option":"system","label":"System","disabled":false,"required":false}]},{"cssClass":"text-muted-foreground","content":"{{ choice | default:'Make a selection' }}","type":"Text"}]},"state":{"radio_13":false,"radio_14":false,"radio_15":true}}} playground=" ">
<CodeGroup>
```python Python icon="python"
from prefab_ui.components import Column, Radio, RadioGroup, Text
from prefab_ui.actions import SetState

with Column(gap=4, css_class="w-fit mx-auto"):
    with RadioGroup(
        on_change=SetState("choice", "You picked: {{ $event }}"),
    ):
        Radio(option="light", label="Light")
        Radio(option="dark", label="Dark")
        Radio(option="system", label="System", value=True)
    Text(
        "{{ choice | default:'Make a selection' }}",
        css_class="text-muted-foreground",
    )
```
```json Protocol icon="brackets-curly"
{
  "view": {
    "cssClass": "gap-4 w-fit mx-auto",
    "type": "Column",
    "children": [
      {
        "name": "radiogroup_5",
        "type": "RadioGroup",
        "onChange": {"action": "setState", "key": "choice", "value": "You picked: {{ $event }}"},
        "children": [
          {
            "name": "radio_13",
            "value": false,
            "type": "Radio",
            "option": "light",
            "label": "Light",
            "disabled": false,
            "required": false
          },
          {
            "name": "radio_14",
            "value": false,
            "type": "Radio",
            "option": "dark",
            "label": "Dark",
            "disabled": false,
            "required": false
          },
          {
            "name": "radio_15",
            "value": true,
            "type": "Radio",
            "option": "system",
            "label": "System",
            "disabled": false,
            "required": false
          }
        ]
      },
      {
        "cssClass": "text-muted-foreground",
        "content": "{{ choice | default:'Make a selection' }}",
        "type": "Text"
      }
    ]
  },
  "state": {"radio_13": false, "radio_14": false, "radio_15": true}
}
```
</CodeGroup>
</ComponentPreview>

For simple "show the current selection" cases, `.rx` is the better approach. Use `on_change` when you need to transform the value or trigger side effects.

## API Reference

<Card icon="code" title="RadioGroup Parameters">
<ParamField body="name" type="str | None" default="None">
  State key for the selected value. Auto-generated if not provided.
</ParamField>

<ParamField body="on_change" type="Action | list[Action] | None" default="None">
  Action(s) to execute when the selection changes. `$event` is the `value` string of the selected radio.
</ParamField>

<ParamField body="css_class" type="str | None" default="None">
  Additional Tailwind CSS classes.
</ParamField>
</Card>

<Card icon="code" title="Radio Parameters">
<ParamField body="option" type="str" required>
  The option identifier stored in the group's state when this radio is selected. Defaults to the label.
</ParamField>

<ParamField body="label" type="str | None" default="None">
  Label text displayed next to the radio button.
</ParamField>

<ParamField body="value" type="bool" default="False">
  Whether this radio is initially selected.
</ParamField>

<ParamField body="disabled" type="bool" default="False">
  Whether this radio is non-interactive.
</ParamField>

<ParamField body="required" type="bool" default="False">
  Whether a selection is required for form submission.
</ParamField>

<ParamField body="css_class" type="str | None" default="None">
  Additional Tailwind CSS classes.
</ParamField>
</Card>

## Protocol Reference

```json RadioGroup
{
  "type": "RadioGroup",
  "children?": "[Component]",
  "let?": "object",
  "name?": "string",
  "onChange?": "Action | Action[]",
  "cssClass?": "string"
}
```

```json Radio
{
  "type": "RadioGroup",
  "children?": "[Component]",
  "let?": "object",
  "name?": "string",
  "onChange?": "Action | Action[]",
  "cssClass?": "string"
}
```

For the complete protocol schema, see [RadioGroup](/protocol/radio-group), [Radio](/protocol/radio-group).
