Radio group
Choose one option from a visible set.
fieldchoice
Preview
Radio group preview
Live · keyboard ready
When to use
- Two to six mutually exclusive options that need descriptions, such as plans.
- Form choices that should submit natively through name.
When not to use
- Use segmented-control for short inline view options.
- Use select for longer lists.
- Use checkbox when several options can be on at once.
shadcn CLI
Also installs (1)
- manicat-foundation
Manual install(+)
registry.jsonjson
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "radio-group",
"type": "registry:ui",
"title": "Radio group",
"description": "Choose one option from a visible set.",
"categories": [
"inputs"
],
"dependencies": [
"motion"
],
"registryDependencies": [
"https://manicat.viraza.net/r/manicat-foundation.json"
],
"files": [
{
"path": "registry/components/radio-group/radio-group.tsx",
"type": "registry:component",
"target": "@components/manicat/radio-group/radio-group.tsx"
},
{
"path": "registry/components/radio-group/radio-group.module.css",
"type": "registry:component",
"target": "@components/manicat/radio-group/radio-group.module.css"
}
],
"docs": "Docs and live preview: https://uiarc.dev/components/radio-group",
"meta": {
"tier": "free",
"kind": "component",
"docs": "https://manicat.viraza.net/components/radio-group",
"markdown": "https://uiarc.dev/components/radio-group/markdown",
"tags": [
"field",
"choice"
]
}
}Usage
Install the component, then copy this into your app. Change the content and the props to fit your screen.
example.tsxtsx
"use client";
import { useState } from "react";
import { RadioGroup } from "@/components/manicat/radio-group/radio-group";
export function RadioGroupExample() {
const [value, setValue] = useState("monthly");
return (
<RadioGroup
label="Billing period"
value={value}
onValueChange={setValue}
options={[
{ value: "monthly", label: "Monthly", description: "Cancel any time." },
{ value: "yearly", label: "Yearly", description: "Two months free." },
]}
/>
);
}
Examples
Real screens rather than isolated props. Switch to Code to copy a whole example into your project.
Billing period Radio options with a description under each label.
Billing period
Radio options with a description under each label. preview
Live · keyboard ready
billing-period-radio-options-with-a-description-under-each-label-.tsxtsx
"use client";
import { useState } from "react";
import { RadioGroup } from "@/components/manicat/radio-group/radio-group";
export function RadioGroupBillingExample() {
const [value, setValue] = useState("monthly");
return (
<RadioGroup
label="Billing period"
value={value}
onValueChange={setValue}
options={[
{ value: "monthly", label: "Monthly", description: "Cancel any time." },
{ value: "yearly", label: "Yearly", description: "Two months free." },
]}
/>
);
}
Keyboard interactions
| Keys | Action |
|---|---|
| Arrow keys | Move the selection between options (native radio behavior). |
| Tab | Enters and leaves the group at the selected option. |
Accessibility
- Uses a fieldset and legend with native radio inputs wrapped in labels.
- The highlight and dot are aria-hidden decoration.
Motion
- One highlight glides to the chosen row on the morph spring while the new dot springs in and the old one shrinks away.
- Reduced motion places the highlight and dot instantly; resizes never animate.
Responsive behavior
- Rows stack vertically and stretch to the column, so long descriptions wrap cleanly on narrow screens.
- A ResizeObserver re-places the highlight on resize without animating it.
Performance
- One shared highlight moves between rows instead of a layout animation per row.
Notes for AI
- Use for two to six mutually exclusive options that need descriptions. Use segmented-control for short inline choices and select for longer lists.
- Always controlled: value and onValueChange are required. Pass name to submit with a form.