Radio group
Pick one option from a set, with a dot that hands off between choices and cards whose selection ring slides to the new pick.
pnpm dlx shadcn@latest add https://hextaui.com/r/radio-group.jsonAdds the component, the HextaUI theme tokens and any HextaUI components it depends on.
Add the theme tokens to your global CSS, if you haven’t yet.
Install the dependencies.
pnpm add @base-ui/react cnCopy and paste the following code into your project.
components/ui/radio-group.tsx lib/motion.ts Update the import paths to match your project setup.
When the choice changes, the old dot shrinks away as the new one springs in, so the eye follows the pick instead of two things blinking at once. Wrap each item in a <label> so the whole row is clickable.
Cards
With variant="card", use RadioGroupCard for options with more to say. The selection ring slides to the new card while its surface fades in.
Horizontal
Cards can sit in a row or any grid. The ring follows them wherever they are.
Form
Inside Field and FieldSet, the group gets a legend, required validation and an error message.
Disabled and read-only
Disable a single option, or make the whole group readOnly to show a value that can't change.
Controlled
Pass value and onValueChange. Outside changes animate the same way as clicks.
Right to left
Radios and cards follow the reading direction.
| Key | Action |
|---|---|
| Tab | Moves focus into the group, onto the picked option or the first one. |
| ↑↓←→ | Moves to the previous or next option and picks it, wrapping at the ends. |
| Space | Picks the focused option. |
- Name the group with
aria-label,aria-labelledbyor aFieldLegend. - Each radio has a 36px hit area, and 44px on touch screens, while staying 16px on screen.
- The dot and card ring don't move with reduced motion.
| Prop | Type | Default |
|---|---|---|
variantcard adds the sliding selection ring. | "default" | "card" | "default" |
value | Value | – |
defaultValue | Value | – |
onValueChange | (value, details) => void | – |
name | string | – |
required | boolean | false |
disabled | boolean | false |
readOnly | boolean | false |
render | ReactElement | (props, state) => ReactElement | <div> |
| Attribute | Description |
|---|---|
data-slot="radio-group" | The group, with data-variant. |
data-slot="radio-group-highlight" | The ring that slides between cards. |
data-disabled | The group is disabled. |
| Prop | Type | Default |
|---|---|---|
value | Value | – |
disabled | boolean | false |
render | ReactElement | (props, state) => ReactElement | <span> |
| Attribute | Description |
|---|---|
data-slot="radio-group-item" | The radio. |
data-checked / data-unchecked | Whether it's the picked option. |
data-disabled / data-readonly | Present when it can't be changed. |
| Prop | Type | Default |
|---|---|---|
value | Value | – |
disabled | boolean | false |
radioClassNameClasses for the radio inside the card. | string | – |
A <label> with the radio built in. Put RadioGroupCardTitle and RadioGroupCardDescription, or anything else, inside.
- MotionThe easing curves, durations and reduced-motion check every component animates with, plus hooks for size morphs and sliding highlights.
- CalendarA date grid for single, range and multiple selection, with sliding months, range previews and touch-sized days.
- CheckboxA checkbox whose check draws in, with indeterminate parents, groups and labels that share its hover.
- ComboboxA filterable select with chips, groups and async results, in a popup that resizes as you type.
- Date pickerA button that opens a calendar in a popover, or a bottom sheet on phones, for single dates and ranges.
- FieldLabels, descriptions and errors wired to their control, with validation states and layouts for forms.