Radio Group
Use radio group with the current published Askr packages.
@askrjs/ui/radio-group0.0.13@askrjs/themes/radio-group0.0.13
How to use radio group
Radio Group should use the standard themed surface unless the application is deliberately implementing its own design system on top of @askrjs/ui.
- Import the themed component from @askrjs/themes/components; use @askrjs/ui directly only when building a custom visual system.
- Keep radio group labels, state, and application actions visible at the call site instead of styling internal DOM nodes.
- Verify keyboard behavior, focus movement or return, disabled state, and both standard themes.
import { FieldLabel, RadioGroup, RadioGroupItem } from '@askrjs/themes/components';
<RadioGroup value={plan()} onValueChange={plan.set}>
<FieldLabel><RadioGroupItem value="team" /> Team</FieldLabel>
<FieldLabel><RadioGroupItem value="business" /> Business</FieldLabel>
</RadioGroup>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 <div role="radiogroup"> wrapping any number of RadioGroupItem children; each RadioGroupItem defaults to a native <button> (not an <input>) 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) => void, disabled?: boolean, name?: string, orientation?: Orientation, and loop?: boolean, layered onto native <div> attributes for RadioGroupProps. RadioGroupItemOwnProps is just value: string (required), disabled?: boolean, and children, with RadioGroupItemProps building on native <button> attributes and RadioGroupItemAsChildProps swapping in asChild: true with a single child.
Edge cases
Because each RadioGroupItem is a <button> by default rather than an <input type="radio">, 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.