Radio group

Choose one option from a visible set.

fieldchoice
Preview
Radio group preview
Billing period

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
Billing period

Keyboard interactions

KeysAction
Arrow keysMove the selection between options (native radio behavior).
TabEnters 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.