Radio groups

Radio groups collect exactly one choice as selectable cards, 6 ready-made variants for options that carry descriptions, prices, people, or logos, built on React Aria’s RadioGroup.

Demo

Change the props and copy the result: the snippet carries the variant’s full items array with its imports, so it compiles as pasted. The breakpoint select is the demo’s own and never reaches the snippet; mobile renders inside a real 375px viewport frame, since the kit’s mobile treatments are viewport media queries.

variant
size
breakpoint
import { ChartBreakoutSquare, MessageChatCircle, Zap } from "@untitledui/icons"; import * as RadioGroups from "@/components/base/radio-groups/radio-groups"; <RadioGroups.RadioButton size="md" aria-label="Choose a plan" defaultValue="basic" items={[ { value: "basic", title: "Basic plan", secondaryTitle: "$10/month", description: "Includes up to 10 users and 20 GB individual data.", icon: Zap }, { value: "business", title: "Business plan", secondaryTitle: "$20/month", description: "Includes up to 20 users, 40 GB data, and analytics.", icon: ChartBreakoutSquare }, { value: "enterprise", title: "Enterprise plan", secondaryTitle: "$40/month", description: "Unlimited users, unlimited data, and priority support.", icon: MessageChatCircle }, ]} />

The family

One barrel, six variants, one item-driven surface: each export takes an items array and renders one selectable card per item over React Aria’s RadioGroup semantics.

The components in this family, with their descriptions and import paths
ComponentDescriptionImport from
RadioGroups (barrel)One namespace re-exporting the six variants under short names. Import it whole and pick with dot access.@/​components/​base/​radio-groups/​radio-groups
RadioGroups.RadioButtonThe plainest card: title, secondary detail, and description behind the familiar radio circle.@/​components/​base/​radio-groups/​radio-groups
RadioGroups.CheckboxThe same card with a rounded-square check glyph. The semantics stay radio; only the glyph changes.@/​components/​base/​radio-groups/​radio-groups
RadioGroups.IconSimpleA featured icon leads each card; the check control moves to the right edge.@/​components/​base/​radio-groups/​radio-groups
RadioGroups.IconCardA two-part card with a display-size price and a badge slot; the only variant with either.@/​components/​base/​radio-groups/​radio-groups
RadioGroups.AvatarA person per card, keyed by id rather than value: assignees, approvers, accounts.@/​components/​base/​radio-groups/​radio-groups
RadioGroups.PaymentIconA payment method with its brand logo, plus built-in Set as default and Edit link buttons.@/​components/​base/​radio-groups/​radio-groups

The card variants

The 6 card-style groups from the radio-groups barrel, all live, matching the Figma sheet’s Type axis. Each takes an items array and renders one selectable card per item: a 1px border at rest, a 2px brand ring when selected. Five of the 6 use the rounded-square check glyph as their control; only the RadioButton variant draws the plain radios’ circle.

RadioGroups.RadioButton
RadioGroups.Checkbox
RadioGroups.IconSimple
RadioGroups.IconCard
RadioGroups.Avatar
RadioGroups.PaymentIcon

A check glyph with radio semantics

Five variants render their selection control with CheckboxBase, the checkbox family’s visual, while the underlying input stays a native radio: exactly one card holds, and assistive tech announces radios. That pairing is the kit’s and the Figma sheet’s, recorded here so nobody patches it away as a bug.

States

Every treatment below is forced through real props: defaultValue for selected and each item’s own disabled flag. Hover and focus have no prop and no exported style object to re-apply, so they are not forced here.

card, rest
card, selected
card, disabled
card, disabled and fading variant (IconSimple)

No hover, and no forceable focus

The kit styles no hover state anywhere in this family, and the Figma sheet agrees: its Hover variants are pixel-identical to Default, measured in the parity audit. The focus treatment cannot be forced statically either, because it lives in render-prop closures rather than an exported style object; Tab through the live cards above to see it drawn on the card and the inner control at once. Note the 2 disabled recipes: RadioButton, Checkbox, and Avatar cards recolor to the disabled background, while IconSimple, IconCard, and PaymentIcon fade to half opacity.