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

Plan
<RadioGroup
  name="plan"
  label="Plan"
  defaultValue="Free"
  items={["Free", "Pro", "Enterprise"]}
/>

Hints + disabled options

Choose a plan

For hobbies

For teams

Contact sales

<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

Size
<RadioGroup
  name="size"
  label="Size"
  defaultValue="m"
  orientation="horizontal"
  items={["S", "M", "L", "XL"]}
/>

Invalid & disabled

Shipping

Choose a shipping method.

Seat (closed)
<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

sm
md
lg
<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

PropTypeDefaultDescription
namestring—Shared radio name; the chosen value posts under it.
items(string | { value, label, hint?, disabled? })[]—Option list; strings become both value and label.
defaultValuestring—Which option starts checked.
labelstring—Group heading rendered in the legend.
orientation"vertical" | "horizontal""vertical"Row layout.
invalidbooleanfalseError-colored legend; visual hook for validation.
ariaDescribedbystring—id of hint/error text describing the group.
disabledbooleanfalseDisable every option.
size"xs" | "sm" | "md" | "lg" | "xl""md"Forwarded to each Radio.
variant"primary" | "secondary" | … | "inherit""primary"Forwarded to each Radio.
classstring—Merged via cn(); Tailwind classes welcome.
idstring—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.