Radio Group

Radio groups let users select a single option from a set of mutually exclusive choices.

Examples

Basic

Secondary

Monthly

Quarterly

Yearly

Disabled

Entire group disabled
Single item disabled

Custom indicator

Controlled

Selected: comfortable

Controlled secondary

MonthlyQuarterlyYearly

Selected: monthly

Overview

RadioGroup provides a set of radio buttons where only one can be selected at a time. Built on Base UI Radio with two visual variants.

tsx
import { RadioGroup, RadioGroupItem } from "@rogo-technologies/ui/radio-group";

<RadioGroup defaultValue="option-one">
  <div className="flex items-center gap-2">
    <RadioGroupItem value="option-one" id="option-one" />
    <label htmlFor="option-one">Option One</label>
  </div>
  <div className="flex items-center gap-2">
    <RadioGroupItem value="option-two" id="option-two" />
    <label htmlFor="option-two">Option Two</label>
  </div>
</RadioGroup>;

Usage

Secondary variant

Pill/tab toggle style. Pass variant="secondary" to both RadioGroup and RadioGroupItem. Children become the visible label.

tsx
<RadioGroup variant="secondary" defaultValue="monthly">
  <RadioGroupItem variant="secondary" value="monthly">Monthly</RadioGroupItem>
  <RadioGroupItem variant="secondary" value="quarterly">Quarterly</RadioGroupItem>
  <RadioGroupItem variant="secondary" value="yearly">Yearly</RadioGroupItem>
</RadioGroup>

Controlled

tsx
const [value, setValue] = useState("comfortable");

<RadioGroup value={value} onValueChange={setValue}>
  <RadioGroupItem value="default" id="option-default" />
  <RadioGroupItem value="comfortable" id="option-comfortable" />
  <RadioGroupItem value="compact" id="option-compact" />
</RadioGroup>;

Disabled

tsx
<RadioGroup defaultValue="option-a" disabled>
  <RadioGroupItem value="option-a" />
  <RadioGroupItem value="option-b" />
</RadioGroup>

<RadioGroup defaultValue="available">
  <RadioGroupItem value="available" />
  <RadioGroupItem value="unavailable" disabled />
</RadioGroup>

Composable API

Use RadioGroupRoot, RadioItem, and RadioIndicator for custom indicator content.

tsx
import { RadioGroupRoot, RadioItem, RadioIndicator } from "@rogo-technologies/ui/radio-group";

<RadioGroupRoot defaultValue="option-a">
  <RadioItem value="option-a">
    <RadioIndicator />
  </RadioItem>
</RadioGroupRoot>

<RadioGroupRoot defaultValue="option-a">
  <RadioItem value="option-a">
    <RadioIndicator>
      <span className="text-brand text-[10px]"></span>
    </RadioIndicator>
  </RadioItem>
</RadioGroupRoot>

With form libraries

tsx
import { useForm, Controller } from "react-hook-form";

const { control, handleSubmit } = useForm({ defaultValues: { plan: "free" } });

<form onSubmit={handleSubmit(onSubmit)}>
  <Controller
    control={control}
    name="plan"
    render={({ field }) => (
      <RadioGroup value={field.value} onValueChange={field.onChange}>
        <RadioGroupItem value="free" id="plan-free" />
        <label htmlFor="plan-free">Free</label>
        <RadioGroupItem value="pro" id="plan-pro" />
        <label htmlFor="plan-pro">Pro</label>
      </RadioGroup>
    )}
  />
</form>;

Accessibility

  • Radio group uses role="radiogroup" with arrow key navigation
  • Each radio is keyboard accessible with Space to select
  • Focus is visible with a ring indicator
  • Associates with labels via id and htmlFor
  • Supports aria-labelledby for group labels
tsx
<RadioGroup aria-labelledby="plan-heading">
  <h3 id="plan-heading">Choose a plan</h3>
  <RadioGroupItem value="free" id="plan-free" />
  <label htmlFor="plan-free">Free</label>
</RadioGroup>

API

RadioGroup

PropTypeDefaultDescription
variant"primary" | "secondary"Visual style of the group container.
valuestringControlled selected value.
defaultValuestringInitial value for uncontrolled usage.
onValueChange(value: string) => voidCallback when selection changes.
disabledbooleanfalseDisables all items in the group.
requiredbooleanfalseMarks the group as required.
namestringName attribute for form submission.

RadioGroupItem

PropTypeDefaultDescription
variant"primary" | "secondary""primary"Visual style of the radio item.
valuestringUnique value identifying this radio.
disabledbooleanfalseDisables this item.
childrenReactNodeLabel content (rendered for secondary variant).

RadioGroupRoot

PropTypeDefaultDescription
valuestringControlled selected value.
defaultValuestringInitial value for uncontrolled usage.
onValueChange(value: string) => voidCallback when selection changes.
disabledbooleanfalseDisables all items.

RadioItem

PropTypeDefaultDescription
valuestringUnique value identifying this radio.
disabledbooleanfalseDisables this item.

RadioIndicator

PropTypeDefaultDescription
childrenReactNodeFilled circleCustom indicator content.
keepMountedbooleanfalseKeep in DOM when unchecked.

Guidelines

Do

  • Always pair radio items with visible labels
  • Use radio groups for mutually exclusive options (only one can be selected)
  • Provide a sensible default selection when possible
  • Group related options together visually
  • Use the secondary variant for compact inline toggles (e.g. billing period)

Don't

  • Don't use radio buttons when multiple selections are allowed — use checkboxes instead
  • Don't use radio groups for binary on/off toggles — use a switch instead
  • Don't have more than 7 options in a radio group — consider a select or combobox instead
  • Don't mix primary and secondary variants in the same group
  • Don't leave a required radio group without a default — users can't deselect
Made in NYC© 2026 Rogo Technologies Inc.