# Radio Group

> Radio Group: anatomy, keyboard behavior, state, and theming in Askr.

Source: [https://askrjs.com/docs/components/radio-group](https://askrjs.com/docs/components/radio-group)

Status: stable. Packages: @askrjs/ui/radio-group, @askrjs/themes/radio-group.

**Published packages are authoritative.** Examples may lag behind a published contract. When guidance differs, verify the exports and TypeScript declarations in your installed package, then file an issue.

## Example

```tsx
import { FieldLabel, RadioGroup, RadioGroupItem } from '@askrjs/themes/components';

<RadioGroup value={plan()} onValueChange={setPlan}>
  <FieldLabel><RadioGroupItem value="team" /> Team</FieldLabel>
  <FieldLabel><RadioGroupItem value="business" /> Business</FieldLabel>
</RadioGroup>
```

## Published props

Generated from the TypeScript declarations shipped by the installed package.

### `RadioGroupItemAsChildProps`

Import from `@askrjs/ui/radio-group`.

- `asChild: true;`
- `children: JSXElement;`
- `disabled?: boolean | undefined;`
- `ref?: Ref<Element>;`
- `value: string;`

### `RadioGroupItemOwnProps`

Import from `@askrjs/ui/radio-group`.

- `children?: unknown;`
- `disabled?: boolean | undefined;`
- `value: string;`

### `RadioGroupItemProps`

Import from `@askrjs/ui/radio-group`.

- `asChild?: false | undefined;`
- `children?: unknown;`
- `ref?: Ref<HTMLButtonElement>;`
- `value: string;`

### `RadioGroupOwnProps`

Import from `@askrjs/ui/radio-group`.

- `children?: unknown;` — Supports literal, nested, array-mapped, and `For`-rendered descendants.
- `defaultValue?: string | undefined;`
- `disabled?: boolean | undefined;`
- `loop?: boolean | undefined;`
- `name?: string | undefined;`
- `onValueChange?: ((value: string) => void) | undefined;`
- `orientation?: Orientation | undefined;`
- `value?: string | undefined;`

### `RadioGroupProps`

Import from `@askrjs/ui/radio-group`.

- `children?: unknown;` — Supports literal, nested, array-mapped, and `For`-rendered descendants.
- `defaultValue?: string | undefined;`
- `disabled?: boolean | undefined;`
- `loop?: boolean | undefined;`
- `name?: string | undefined;`
- `onValueChange?: ((value: string) => void) | undefined;`
- `orientation?: Orientation | undefined;`
- `ref?: Ref<HTMLDivElement>;`
- `value?: string | undefined;`

## Purpose

Choosing exactly one value from a set is what RadioGroup and RadioGroupItem are built for together, with the former acting as container and the latter representing each option. Unlike a collection of independent Checkboxes, this pairing means the container owns the shared value and coordinates which single item is selected. The result mirrors the roving-focus behavior users already expect from native radio buttons.

## Install and import

Getting Radio Group running requires @askrjs/ui and @askrjs/askr for the headless primitives, plus @askrjs/themes if default styling matters to you. Both `RadioGroup` and `RadioGroupItem` are available headless from `@askrjs/ui/radio-group`, or pre-styled from `@askrjs/themes/radio-group` using the same two names. Themes re-exports both components straight from @askrjs/ui rather than wrapping them, so the styled and headless versions behave identically.

## Live examples

A basic vertical group with three or four options is the starting point; from there, try a horizontal orientation, a disabled item mixed in with enabled ones, and a fully disabled group. An asChild example on a RadioGroupItem is also worth checking since, unlike RadioGroup's container, items support polymorphic rendering.

## Anatomy

RadioGroup renders a single &lt;div role="radiogroup"&gt; wrapping any number of RadioGroupItem children; each RadioGroupItem defaults to a native &lt;button&gt; (not an &lt;input&gt;) with role="radio", which is why RadioGroupItemProps omits onClick, type, and value from the native button attributes it otherwise passes through. The group also renders a hidden native input for form integration, per FORM_INTEGRATION.hiddenInputType in its accessibility contract, so the selected value still participates in a plain HTML form submission.

## State model

The selected value lives on RadioGroup itself — value + onValueChange for controlled use, or defaultValue for uncontrolled — and each RadioGroupItem only declares the value it represents plus its own disabled flag. Individual items don't track checked state locally; whether an item renders as selected is derived by comparing its value prop against the group's current value.

## Keyboard and accessibility

RadioGroup implements roving focus: the selected item (or the first enabled one) has tabIndex 0 and the rest have tabIndex -1, and ArrowLeft/ArrowRight/ArrowUp/ArrowDown move selection between items, matching the WAI-ARIA radio group pattern. The container carries role="radiogroup" and aria-orientation, while each item carries role="radio" and aria-checked, and the whole group can be set to loop or stop at the ends via the loop prop.

## Styling and tokens

The default theme styles [data-slot="radio-group"] as a responsive grid (auto-fit columns when horizontal) and renders each [data-slot="radio-group-item"] as a full card-style row — border, padding, and a hand-drawn radio dot appended via ::before — rather than a bare native radio circle. That means the themed radio group looks more like a selectable list than a classic row of small circles; data-orientation controls whether items lay out as a single column or a wrapping grid.

## API

RadioGroupOwnProps covers value?: string, defaultValue?: string, onValueChange?: (value: string) =&gt; void, disabled?: boolean, name?: string, orientation?: Orientation, and loop?: boolean, layered onto native &lt;div&gt; attributes for RadioGroupProps. RadioGroupItemOwnProps is just value: string (required), disabled?: boolean, and children, with RadioGroupItemProps building on native &lt;button&gt; attributes and RadioGroupItemAsChildProps swapping in asChild: true with a single child.

## Edge cases

Because each RadioGroupItem is a &lt;button&gt; by default rather than an &lt;input type="radio"&gt;, you can't rely on native form serialization from the visible items alone — that's exactly why the group renders its own hidden input to carry the value into a form submit. Disabling the group's own disabled prop cascades to every item, but disabling one RadioGroupItem individually while leaving the group and its siblings enabled works too — check which level you actually want before setting the prop.

## Related pages

See Checkbox for independent (non-exclusive) toggles, Select for choosing one value from a larger or less immediately visible list, and Form, Label, Field, and Input Group for grouping RadioGroup under a shared legend and description.

## Documentation navigation

[Previous](https://askrjs.com/docs/components/checkbox/index.md) | [Next](https://askrjs.com/docs/components/select/index.md)
