# Radio Group

URL: https://ark-ui.com/docs/components/radio-group
Source: https://raw.githubusercontent.com/chakra-ui/ark/refs/heads/main/website/src/content/pages/components/radio-group.mdx

Allows single selection from multiple options.

---

<ComponentPreview id="RadioGroup" />

## Anatomy

<Anatomy id="radio-group" />

```tsx
<RadioGroup.Root>
  <RadioGroup.Label />
  <RadioGroup.Indicator />
  <RadioGroup.Item>
    <RadioGroup.ItemControl />
    <RadioGroup.ItemText />
    <RadioGroup.ItemHiddenInput />
  </RadioGroup.Item>
</RadioGroup.Root>
```

## Examples

**Example: basic**

```ripple
import { RadioGroup } from 'ark-ripple/radio-group';
import styles from 'styles/radio-group.module.css';

const frameworks = ['React', 'Solid', 'Vue'];

export component Basic() {
  <RadioGroup.Root class={styles.Root} defaultValue="React">
    <RadioGroup.Label class={styles.Label}>{'Framework'}</RadioGroup.Label>
    for (const framework of frameworks; key framework) {
      <RadioGroup.Item class={styles.Item} value={framework}>
        <RadioGroup.ItemControl class={styles.ItemControl} />
        <RadioGroup.ItemText class={styles.ItemText}>{framework}</RadioGroup.ItemText>
        <RadioGroup.ItemHiddenInput />
      </RadioGroup.Item>
    }
  </RadioGroup.Root>
}

```

### Initial Value

To set the radio group's initial value, set the `defaultValue` prop to the value of the radio item to be selected by
default.

**Example: initial-value**

```ripple
import { RadioGroup } from 'ark-ripple/radio-group';
import styles from 'styles/radio-group.module.css';

const frameworks = ['React', 'Solid', 'Vue'];

export component InitialValue() {
  <RadioGroup.Root class={styles.Root} defaultValue="Solid">
    <RadioGroup.Label class={styles.Label}>{'Framework'}</RadioGroup.Label>
    for (const framework of frameworks; key framework) {
      <RadioGroup.Item class={styles.Item} value={framework}>
        <RadioGroup.ItemControl class={styles.ItemControl} />
        <RadioGroup.ItemText class={styles.ItemText}>{framework}</RadioGroup.ItemText>
        <RadioGroup.ItemHiddenInput />
      </RadioGroup.Item>
    }
  </RadioGroup.Root>
}

```

### Controlled

For a controlled Radio Group, the state is managed using the `value` prop, and updates when the `onValueChange` event
handler is called:

**Example: controlled**

```ripple
import { RadioGroup } from 'ark-ripple/radio-group';
import { track } from 'ripple';
import styles from 'styles/radio-group.module.css';

const frameworks = ['React', 'Solid', 'Vue'];

export component Controlled() {
  let value = track<string | null>(null);

  <RadioGroup.Root
    class={styles.Root}
    {value}
    onValueChange={(e) => {
      @value = e.value;
    }}
  >
    <RadioGroup.Label class={styles.Label}>{'Framework'}</RadioGroup.Label>
    for (const framework of frameworks; key framework) {
      <RadioGroup.Item class={styles.Item} value={framework}>
        <RadioGroup.ItemControl class={styles.ItemControl} />
        <RadioGroup.ItemText class={styles.ItemText}>{framework}</RadioGroup.ItemText>
        <RadioGroup.ItemHiddenInput />
      </RadioGroup.Item>
    }
  </RadioGroup.Root>
}

```

### Root Provider

An alternative way to control the radio group is to use the `RootProvider` component and the `useRadioGroup` hook. This
way you can access the state and methods from outside the component.

**Example: root-provider**

```ripple
import { RadioGroup, useRadioGroup } from 'ark-ripple/radio-group';
import button from 'styles/button.module.css';
import styles from 'styles/radio-group.module.css';

const frameworks = ['React', 'Solid', 'Vue'];

export component RootProvider() {
  const radioGroup = useRadioGroup({ defaultValue: 'React' });

  <div class="stack">
    <RadioGroup.RootProvider class={styles.Root} value={radioGroup}>
      <RadioGroup.Label class={styles.Label}>{'Framework'}</RadioGroup.Label>
      for (const framework of frameworks; key framework) {
        <RadioGroup.Item class={styles.Item} value={framework}>
          <RadioGroup.ItemControl class={styles.ItemControl} />
          <RadioGroup.ItemText class={styles.ItemText}>{framework}</RadioGroup.ItemText>
          <RadioGroup.ItemHiddenInput />
        </RadioGroup.Item>
      }
    </RadioGroup.RootProvider>
    <button class={button.Root} onClick={() => @radioGroup.setValue('Solid')}>
      {'Set to Solid'}
    </button>
  </div>
}

```

### Disabled

To make a radio group disabled, set the `disabled` prop to `true`.

**Example: disabled**

```ripple
import { RadioGroup } from 'ark-ripple/radio-group';
import styles from 'styles/radio-group.module.css';

const frameworks = ['React', 'Solid', 'Vue'];

export component Disabled() {
  <RadioGroup.Root class={styles.Root} defaultValue="React" disabled>
    <RadioGroup.Label class={styles.Label}>{'Framework'}</RadioGroup.Label>
    for (const framework of frameworks; key framework) {
      <RadioGroup.Item class={styles.Item} value={framework}>
        <RadioGroup.ItemControl class={styles.ItemControl} />
        <RadioGroup.ItemText class={styles.ItemText}>{framework}</RadioGroup.ItemText>
        <RadioGroup.ItemHiddenInput />
      </RadioGroup.Item>
    }
  </RadioGroup.Root>
}

```

## Guides

### asChild

The `RadioGroup.Item` component renders as a `label` element by default. This ensures proper form semantics and
accessibility, as radio groups are form controls that require labels to provide meaningful context for users.

When using the `asChild` prop, you must **render a `label` element** as the direct child of `RadioGroup.Item` to
maintain valid HTML structure and accessibility compliance.

```tsx
// INCORRECT usage ❌
<RadioGroup.Item asChild>
  <div>
    <RadioGroup.ItemHiddenInput />
    <RadioGroup.ItemText>
      <RadioGroup.ItemControl />
    </RadioGroup.ItemText>
  </div>
</RadioGroup.Item>

// CORRECT usage ✅
<RadioGroup.Item asChild>
  <label>
    <RadioGroup.ItemHiddenInput />
    <RadioGroup.ItemText>
      <RadioGroup.ItemControl />
    </RadioGroup.ItemText>
  </label>
</RadioGroup.Item>
```

### Hidden Input

The `RadioGroup.ItemHiddenInput` component renders a hidden HTML input element that enables proper form submission and
integration with native form behaviors. This component is essential for the radio group to function correctly as it:

- Provides the underlying input element that browsers use for form submission
- Enables integration with form libraries and validation systems
- Ensures the radio group works with native form reset functionality

```tsx
// INCORRECT usage ❌
<RadioGroup.Item>
  <RadioGroup.ItemText>
    <RadioGroup.ItemControl />
  </RadioGroup.ItemText>
</RadioGroup.Item>

// CORRECT usage ✅
<RadioGroup.Item>
  <RadioGroup.ItemHiddenInput />
  <RadioGroup.ItemText>
    <RadioGroup.ItemControl />
  </RadioGroup.ItemText>
</RadioGroup.Item>
```

## API Reference

### Props

**Component API Reference**

**Root Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `defaultValue` | `string` | No | The initial value of the checked radio when rendered.
Use when you don't need to control the value of the radio group. |
| `disabled` | `boolean` | No | If `true`, the radio group will be disabled |
| `form` | `string` | No | The associate form of the underlying input. |
| `id` | `string` | No | The unique identifier of the machine. |
| `ids` | `Partial<{
  root: string
  label: string
  indicator: string
  item: (value: string) => string
  itemLabel: (value: string) => string
  itemControl: (value: string) => string
  itemHiddenInput: (value: string) => string
}>` | No | The ids of the elements in the radio. Useful for composition. |
| `invalid` | `boolean` | No | If `true`, the radio group is marked as invalid. |
| `name` | `string` | No | The name of the input fields in the radio
(Useful for form submission). |
| `onValueChange` | `(details: ValueChangeDetails) => void` | No | Function called once a radio is checked |
| `orientation` | `'horizontal' | 'vertical'` | No | Orientation of the radio group |
| `readOnly` | `boolean` | No | Whether the radio group is read-only |
| `required` | `boolean` | No | If `true`, the radio group is marked as required. |
| `value` | `string` | No | The controlled value of the radio group |


**Root Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | radio-group |
| `[data-part]` | root |
| `[data-orientation]` | The orientation of the radio-group |
| `[data-disabled]` | Present when disabled |
| `[data-invalid]` | Present when invalid |
| `[data-required]` | Present when required |


**Indicator Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Indicator Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | radio-group |
| `[data-part]` | indicator |
| `[data-disabled]` | Present when disabled |
| `[data-orientation]` | The orientation of the indicator |


**Indicator CSS Variables:**

| Variable | Description |
|----------|-------------|
| `--transition-property` | The transition property value for the Indicator |
| `--left` | The left position value |
| `--top` | The top position value |
| `--width` | The width of the element |
| `--height` | The height of the element |


**ItemControl Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**ItemControl Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | radio-group |
| `[data-part]` | item-control |
| `[data-active]` | Present when active or pressed |


**ItemHiddenInput Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Item Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `value` | `string` | Yes |  |
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `disabled` | `boolean` | No |  |
| `invalid` | `boolean` | No |  |


**ItemText Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Label Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Label Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | radio-group |
| `[data-part]` | label |
| `[data-orientation]` | The orientation of the label |
| `[data-disabled]` | Present when disabled |
| `[data-invalid]` | Present when invalid |
| `[data-required]` | Present when required |


**RootProvider Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `value` | `UseRadioGroupReturn` | Yes |  |
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


### Context

**API:**

| Property | Type | Description |
|----------|------|-------------|
| `value` | `string` | The current value of the radio group |
| `setValue` | `(value: string) => void` | Function to set the value of the radio group |
| `clearValue` | `VoidFunction` | Function to clear the value of the radio group |
| `focus` | `VoidFunction` | Function to focus the radio group |
| `getItemState` | `(props: ItemProps) => ItemState` | Returns the state details of a radio input |


## Accessibility

Complies with the [Radio WAI-ARIA design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/radio/).

### Keyboard Support

<KeyBindingsTable id="radio-group" />