Radio cards
Selectable option cards with a sliding selection ring, price and description slots, and radio keyboard behavior.
pnpm dlx shadcn@latest add @uiarc/radio-cardsLive · keyboard ready
- Choices where each option needs a description, price, or estimate to decide.
- Plan, shipping, region, or size pickers inside checkout and setup flows.
- Options that may be unavailable and need a visible reason.
- Use radio-group for short text-only options.
- Use segmented-control for two to four view modes that switch content instantly.
- Use select or combobox for long lists.
Installation
Add Radio cards with the shadcn CLI, or copy the source by hand.
pnpm dlx shadcn@latest add @uiarc/radio-cardsAdds the component and its local dependencies, and installs motion. First time? Add the @uiarc registry to components.json, or use the full URL:
example.tsx
import { RadioCards } from "@/registry/components/radio-cards/radio-cards"; export function ShippingSpeed() { return ( <RadioCards aria-label="Shipping speed" name="shipping" layout="list" defaultValue="standard" options={[ { value: "standard", label: "Standard", description: "4 to 6 business days", meta: "Free" }, { value: "express", label: "Express", description: "2 business days", meta: "$12" }, { value: "overnight", label: "Overnight", meta: "$29", disabled: true, disabledReason: "Not available for this address" }, ]} /> );}RadioCards
Selectable option cards for choices that need more than a label. One selection ring glides from card to card, and the group behaves as a native radio group.
PropTypeDefaultDescription
optionsRequiredRadioCardOption[]–Choices: { value, label, description?, meta?, icon?, disabled?, disabledReason? }.valuestring | null–Selected value (controlled).defaultValuestring | nullnullInitial value when uncontrolled.onValueChange(value: string) => void–Called when a new card is selected.layout"grid" | "list""grid""grid" places cards in responsive columns; "list" stacks full-width rows with meta at the end.minColumnWidthnumber180Narrowest a grid column may get before the grid drops a column, in px.namestring–Form field name. Renders a hidden input with the selected value.requiredboolean–Marks the group and hidden input as required.disabledbooleanfalseDisables every card....propsHTMLAttributes<HTMLDivElement>–Forwarded to the radiogroup, including ref, aria-label, and className.- Tab
- Focuses the selected card, or the first enabled one. One tab stop for the group.
- Arrow keys
- Move to the next or previous enabled card and select it, wrapping. Left and right flip in RTL.
- HomeorEnd
- Select the first or last enabled card.
- EnterorSpace
- Selects the focused card.
- role="radiogroup" with role="radio" cards, aria-checked, and aria-disabled; give the group an aria-label or aria-labelledby.
- Each card is labelled by its label and described by its description, or by disabledReason when disabled.
- Selection is shown by a ring and a filled indicator dot, not color alone.
- One ring springs to the selected card's box and its size; resizes snap it without replaying the glide.
- The indicator dot scales in on the snappy spring.
- Reduced motion jumps the ring and dot to their final state.
- The grid uses auto-fill columns with a minimum of minColumnWidth, so it drops to one column in narrow containers.
- List layout keeps the indicator, text, and meta on one row and truncates nothing; long descriptions wrap.
- One ResizeObserver on the group keeps the ring aligned; the ring moves with motion values without re-rendering cards.
Notes for AI
Give your coding assistant the Markdown reference instead of screenshots.
- Choose it for three to six options that each need a description or price: plans, shipping speeds, regions, instance sizes.
- Pass name to submit with a native form; the value is carried by a hidden input.
- Use disabledReason to explain why an option is unavailable instead of hiding it.
The full library index for assistants is at /llms.txt.