HapplyUI

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-context
  • use-form-field-binding
  • radio
  • tv
  • happly-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.

Choose the plan that works for you.
<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.

PropTypeDefaultDescription
variant'primary' | 'neutral''neutral'Radio indicator color variant
hasErrorbooleanfalseError state — changes ring and focus shadow to error colors
allowDeselectbooleanfalseAllow clicking a selected card to deselect it
valuestring-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.

PropTypeDefaultDescription
valuestring-The radio value for this card
disabledbooleanfalseDisabled state

RadioCard.Content

Flex wrapper for title and description. Takes flex-1 to fill available space.

PropTypeDefaultDescription
inlinebooleanfalseWhen true, renders children in a row (inline) instead of a column

RadioCard.Title

Styled title text. Renders a span element.

PropTypeDefaultDescription

RadioCard.Description

Styled description text. Renders a p element.

PropTypeDefaultDescription

RadioCard.Indicator

The radio circle indicator. Reads value and disabled from the parent Item context.

PropTypeDefaultDescription

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.

PropTypeDefaultDescription
valuestring-The radio value for this card
titleReact.ReactNode-Card title
descriptionReact.ReactNode-Card description
iconReact.ReactNode-Leading element (e.g. KeyIcon.Root or raw icon)
badgeReact.ReactNode-Badge element rendered next to the title
inlinebooleanfalseRender title and description inline
disabledbooleanfalseDisabled state

Previous
Radio
Next
Select