Components
Radio Card
A composable radio input styled as a card. Arrange titles, descriptions, icons, and badges however you want.
Installation
bunx @happlyui/cli@latest add radio-card
Dependencies
npm packages
@radix-ui/react-radio-group
Registry dependencies
These are automatically installed when you add this component.
form-field-contextuse-form-field-bindingradiotvhapply-ui-utils
Usage
import { RadioCard } from "@/components/ui/radio-card"
<RadioCard.Root value={value} onValueChange={setValue}>
<RadioCard
value="opt1"
title="Option 1"
description="Description"
/>
</RadioCard.Root>
Examples
Default
Basic radio cards with title and description.
<RadioCard.Root value={value} onValueChange={setValue}>
<RadioCard.Item value="personal">
<RadioCard.Content>
<RadioCard.Title>Personal</RadioCard.Title>
<RadioCard.Description>For individual use</RadioCard.Description>
</RadioCard.Content>
<RadioCard.Indicator />
</RadioCard.Item>
</RadioCard.Root>
With Key Icons
Icons wrapped in KeyIcon for a styled container.
<RadioCard.Item value="card">
<KeyIcon.Root icon={<RiBankCardLine />} />
<RadioCard.Content>
<RadioCard.Title>Credit Card</RadioCard.Title>
</RadioCard.Content>
<RadioCard.Indicator />
</RadioCard.Item>
With Simple Icons
Raw icons without KeyIcon wrapper.
<RadioCard.Item value="card">
<RiBankCardLine className="w-5 h-5 shrink-0 text-text-sub-600" />
<RadioCard.Content>
<RadioCard.Title>Credit Card</RadioCard.Title>
</RadioCard.Content>
<RadioCard.Indicator />
</RadioCard.Item>
With Badges
Badges placed inline with the title.
<RadioCard.Content>
<div className="flex items-center gap-2">
<RadioCard.Title>Pro Plan</RadioCard.Title>
<Badge.Root variant="light" color="blue" size="small">Popular</Badge.Root>
</div>
<RadioCard.Description>Advanced features</RadioCard.Description>
</RadioCard.Content>
Inline Description
Description rendered inline with the title using the inline prop.
<RadioCard.Content inline>
<RadioCard.Title>Monthly</RadioCard.Title>
<RadioCard.Description>$10/mo</RadioCard.Description>
</RadioCard.Content>
Allow Deselect
Click a selected card to deselect it.
<RadioCard.Root allowDeselect value={value} onValueChange={setValue}>
...
</RadioCard.Root>
Error State
Error styling on the cards.
<RadioCard.Root hasError value={value} onValueChange={setValue}>
...
</RadioCard.Root>
Disabled
Disabled radio cards.
<RadioCard.Item value="opt1" disabled>
...
</RadioCard.Item>
Composed
Shorthand component that pre-assembles Item, Content, Title, Description, and Indicator. Import as { RadioCard } for direct use.
<RadioCard
value="card"
title="Credit Card"
description="Pay with credit card"
icon={<KeyIcon.Root icon={<RiBankCardLine />} />}
/>
Primary Variant
Primary colored radio indicator.
<RadioCard.Root variant="primary" value={value} onValueChange={setValue}>
...
</RadioCard.Root>
With Form Field
Radio cards composed with FormField for consistent label and hint.
<FormField.Root label="Select Plan" required hint="Choose the plan that works for you.">
<RadioCard.Root value={value} onValueChange={setValue}>
<RadioCard.Item value="personal">
<RadioCard.Content>
<RadioCard.Title>Personal</RadioCard.Title>
<RadioCard.Description>For individual use</RadioCard.Description>
</RadioCard.Content>
<RadioCard.Indicator />
</RadioCard.Item>
</RadioCard.Root>
</FormField.Root>
API Reference
RadioCard.Root
The root container wrapping Radix RadioGroup.Root.
| Prop | Type | Default | Description |
|---|---|---|---|
variant | 'primary' | 'neutral' | 'neutral' | Radio indicator color variant |
hasError | boolean | false | Error state — changes ring and focus shadow to error colors |
allowDeselect | boolean | false | Allow clicking a selected card to deselect it |
value | string | - | The controlled value |
onValueChange | (value: string) => void | - | Change callback |
RadioCard.Item
A card container. Renders a label element. Place Content, icons, badges, and Indicator as children.
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | - | The radio value for this card |
disabled | boolean | false | Disabled state |
RadioCard.Content
Flex wrapper for title and description. Takes flex-1 to fill available space.
| Prop | Type | Default | Description |
|---|---|---|---|
inline | boolean | false | When true, renders children in a row (inline) instead of a column |
RadioCard.Title
Styled title text. Renders a span element.
| Prop | Type | Default | Description |
|---|
RadioCard.Description
Styled description text. Renders a p element.
| Prop | Type | Default | Description |
|---|
RadioCard.Indicator
The radio circle indicator. Reads value and disabled from the parent Item context.
| Prop | Type | Default | Description |
|---|
RadioCard
Shorthand that pre-assembles Item + Content + Title + Description + Indicator. Import as { RadioCard } for direct use, or access as RadioCard.RadioCard via namespace import. Use primitives directly for custom layouts.
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | - | The radio value for this card |
title | React.ReactNode | - | Card title |
description | React.ReactNode | - | Card description |
icon | React.ReactNode | - | Leading element (e.g. KeyIcon.Root or raw icon) |
badge | React.ReactNode | - | Badge element rendered next to the title |
inline | boolean | false | Render title and description inline |
disabled | boolean | false | Disabled state |