Radio group

Single-select list with a sliding highlight, spring radio control, and optional descriptions. Built with Framer Motion and your theme tokens.

Live preview

One choice per group · Motion follows selection · Copy-paste friendly

Install

v0

Ship the registry bundle to v0 and iterate on motion or layout with prompts.

Usage

Default export — import RadioGroup from @/components/ui/radiogroup. See tile 05 for the full API and registry install URL.

tsx
import RadioGroup from "@/components/ui/radiogroup";
import { useState } from "react";

const options = [
  {
    value: "monthly",
    label: "Monthly",
    description: "Cancel anytime",
  },
  {
    value: "yearly",
    label: "Yearly",
    description: "Two months free",
  },
];

export function BillingCycle() {
  const [value, setValue] = useState("monthly");

  return (
    <RadioGroup onChange={setValue} options={options} value={value} />
  );
}

Dependencies

Registry peers and props — file name radiogroup.tsx, default export RadioGroup.

options (required)

Array of objects with value (stable id), label (primary line), and optional description (muted subline). The first option’s value is used as the initial selection when uncontrolled.

value (optional)

Controlled selected value as a string matching one option.value. Pair with onChange for full control; when omitted, internal state starts from the first option.

onChange (optional)

Called with the chosen option value when the user selects a row. Use with value for controlled mode.

className (optional)

Forwarded to the root radiogroup wrapper for max-width, spacing, or layout in forms.

Framer Motion

Staggered row entrance, hover nudge and tap scale, shared layoutId sliding background for the active row, and spring-driven ring/dot on the radio control. The highlight uses layoutId "radio-active-bg" — if you render multiple RadioGroup instances on the same page, duplicate that id can cross-animate; fork the file and give each group a unique layoutId when needed.

Accessibility

Root has role="radiogroup"; each row is a button with role="radio" and aria-checked. For production forms, pair with a visible legend or label and consider roving tabindex if you extend keyboard behavior.

shadcn registry

Install adds radiogroup.tsx under components/ui; the registry item declares framer-motion as the peer dependency.

npx shadcn@latest add https://iconiqui.com/r/radiogroup.json