Forms · astro-ui · RadioGroup
Radio Group
Declarative single-choice group — fieldset of radios from a plain items array.
RadioGroup renders a labelled fieldset of Radio components from a plain items array — native radios underneath, so the browser provides single-choice semantics and arrow-key navigation with zero JavaScript. The group centralizes what hand-written fieldsets repeat: defaultValue picks the initially checked option (only one carries checked), orientation lays rows out vertically or horizontally, options can carry a muted hint line or be individually disabled, and group-level invalid styling ties the legend to your error text via ariaDescribedby. For fully custom per-option markup, hand-write the fieldset with Radio components instead.
Features
- Declarative `items` array — strings or {value, label, hint?, disabled?}
- defaultValue sets the initially checked option (server-rendered)
- orientation: vertical stack or horizontal wrap
- Per-option muted hint lines and disabled state
- Group-level invalid legend styling + ariaDescribedby wiring
- Native radios underneath — arrows navigate, forms submit natively
- size/variant forwarded to every option (mirrors Radio)
Usage
---
import { RadioGroup } from "astro-ui";
---
<RadioGroup
name="plan"
label="Plan"
defaultValue="pro"
items={[
{ value: "free", label: "Free", hint: "For hobbies" },
{ value: "pro", label: "Pro", hint: "For teams" },
]}
/>Demos
Basic
<RadioGroup
name="plan"
label="Plan"
defaultValue="Free"
items={["Free", "Pro", "Enterprise"]}
/>Hints + disabled options
<RadioGroup
name="plan"
label="Choose a plan"
defaultValue="pro"
items={[
{ value: "free", label: "Free", hint: "For hobbies" },
{ value: "pro", label: "Pro", hint: "For teams" },
{ value: "enterprise", label: "Enterprise", hint: "Contact sales", disabled: true },
]}
/>Horizontal
<RadioGroup
name="size"
label="Size"
defaultValue="m"
orientation="horizontal"
items={["S", "M", "L", "XL"]}
/>Invalid & disabled
Choose a shipping method.
<RadioGroup
name="ship"
label="Shipping"
invalid
ariaDescribedby="ship-err"
items={["Standard", "Express"]}
/>
<p id="ship-err">Choose a shipping method.</p>
<RadioGroup name="seat" label="Seat (closed)" disabled items={["Window", "Aisle"]} />Sizes
<RadioGroup size="sm" name="s1" label="sm" items={["One", "Two"]} />
<RadioGroup size="md" name="s2" label="md" items={["One", "Two"]} />
<RadioGroup size="lg" name="s3" label="lg" items={["One", "Two"]} />API
Props
| Prop | Type | Default | Description |
|---|---|---|---|
name | string | — | Shared radio name; the chosen value posts under it. |
items | (string | { value, label, hint?, disabled? })[] | — | Option list; strings become both value and label. |
defaultValue | string | — | Which option starts checked. |
label | string | — | Group heading rendered in the legend. |
orientation | "vertical" | "horizontal" | "vertical" | Row layout. |
invalid | boolean | false | Error-colored legend; visual hook for validation. |
ariaDescribedby | string | — | id of hint/error text describing the group. |
disabled | boolean | false | Disable every option. |
size | "xs" | "sm" | "md" | "lg" | "xl" | "md" | Forwarded to each Radio. |
variant | "primary" | "secondary" | … | "inherit" | "primary" | Forwarded to each Radio. |
class | string | — | Merged via cn(); Tailwind classes welcome. |
id | string | — | Element id. |
Accessibility
Implemented accessibility features — semantics, ARIA wiring, and keyboard behavior that ship with the component.
- Real <fieldset>/<legend> group semantics — the legend names the group for screen readers
- Native radio keyboard behavior: arrows move within the group, Tab enters/exits
- Hint paragraphs get ids for aria-describedby wiring at the group level
- Invalid state marks the legend visually; pair with ariaDescribedby pointing at error text
- The required indicator is text (" *" in the legend), never color alone
FAQ
How is this different from Radio?
Radio is one control — you hand-write the fieldset/legend and repeat it per option. RadioGroup takes an items array and renders the whole labelled group, centralizing defaultValue, hints, orientation, and invalid wiring. Both are zero-JS; RadioGroup renders Radio components underneath.
Can I use custom markup per option?
RadioGroup options are label + hint text. For rich option content (icons, price tables, cards), hand-write the fieldset with individual Radio components — the native group semantics come from sharing `name`, not from the wrapper.