Skip to content
Primitives

Radio Group

Sharp radio group with a signal indicator.

● LIVE
npx shadcn@latest add https://okibi.cndr.dev/r/radio-group.json

Installation

Install the theme first, then add the component:

npx shadcn@latest add https://okibi.cndr.dev/r/radio-group.json

Install the required dependencies:

npm install @radix-ui/react-radio-group lucide-react

Copy the source from the Code tab above into components/ui/radio-group.tsx. It uses the cn helper and the okibi theme tokens — install the theme first if you haven't.

Usage

import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"

<RadioGroup defaultValue="alpha">
  <RadioGroupItem value="alpha" id="r-alpha" />
  <RadioGroupItem value="bravo" id="r-bravo" />
</RadioGroup>

Anatomy

<RadioGroup defaultValue="alpha">
  {/* one row per choice */}
  <div className="flex items-center gap-2">
    <RadioGroupItem value="alpha" id="r-alpha" />
    <Label htmlFor="r-alpha">Alpha</Label>
  </div>
  <div className="flex items-center gap-2">
    <RadioGroupItem value="bravo" id="r-bravo" />
    <Label htmlFor="r-bravo">Bravo</Label>
  </div>
</RadioGroup>
  • RadioGroup — the role='radiogroup' container; owns the selected value.
  • RadioGroupItem — one square option; its value is what the group reports when checked.
  • Label — paired by htmlFor to each item's id for the accessible name and click target.

Examples

Disabled

Disable a single RadioGroupItem to lock one option, or disable the root to lock the whole group.

● LIVE

With descriptions

Pair each option with a Label and a muted helper line for settings-style rows.

● LIVE

Suppress non-critical alerts and run dark.

Route signals normally with standard thresholds.

Escalate every anomaly and pre-arm countermeasures.

Invalid

Set aria-invalid on the group to flip the items to the destructive keyline for validation errors.

● LIVE

SEC // select an operating posture to continue.

Accessibility

  • Built on Radix RadioGroup.Root / Item, giving role='radiogroup' with roving tabindex over the items.
  • Arrow keys move focus and select within the group; only the checked item is in the tab order.
  • Selection is reflected via data-state='checked', which fills the item signal and shows the core dot.
  • aria-invalid='true' on an item flips it to the destructive keyline (border-destructive, ring-destructive).
  • Items render no text — give each RadioGroupItem an id and pair a Label via htmlFor for the name and touch target.
  • Focus shows the 2px signal ring (focus-visible:ring-2 ring-ring ring-offset-2); disabled items are opacity-60 and skipped.

Guidelines

Do

  • Pair each RadioGroupItem with a Label so the label text is part of the hit area.
  • Use a radio group for one-of-many selection where every option should stay visible.
  • Set a sensible defaultValue (or controlled value) so the group is never empty on first paint.

Don't

  • Don't use a radio group for independent on/off toggles — that's Checkbox.
  • Don't offer a single radio with no alternative; if there's one option, use a checkbox.
  • Don't rely on color alone for the selected state — the core dot and label carry it too.

On this page