Skip to content

Fields

Radio

A set of choices where one choice wins.

When to use it: Use it for one choice from a short visible list.

On this page

Example

Radio.tsxtsx
import { Radio, RadioGroup } from "@comp0/react";

export function Example() {
  return (
    <RadioGroup
      className="flex flex-col gap-2 sm:flex-row sm:gap-4"
      defaultValue="standard"
      name="shipping"
    >
      <Radio
        value="standard"
        className="group flex min-h-7 items-center gap-2 rounded px-1 text-base text-zinc-800 data-selected:bg-teal-50 data-selected:text-teal-950 sm:text-sm dark:text-zinc-100 dark:data-selected:bg-teal-950/40 dark:data-selected:text-teal-50"
      >
        <span className="grid size-5 shrink-0 place-items-center rounded-full border border-zinc-950/20 bg-white ring-2 ring-transparent group-data-focused:ring-teal-600 group-data-selected:border-teal-600 sm:size-4 dark:border-white/20 dark:bg-zinc-900 dark:group-data-focused:ring-teal-400 dark:group-data-selected:border-teal-400">
          <span className="size-2 rounded-full bg-teal-600 opacity-0 group-data-selected:opacity-100 dark:bg-teal-400" />
        </span>
        Standard
      </Radio>
      <Radio
        value="express"
        className="group flex min-h-7 items-center gap-2 rounded px-1 text-base text-zinc-800 data-selected:bg-teal-50 data-selected:text-teal-950 sm:text-sm dark:text-zinc-100 dark:data-selected:bg-teal-950/40 dark:data-selected:text-teal-50"
      >
        <span className="grid size-5 shrink-0 place-items-center rounded-full border border-zinc-950/20 bg-white ring-2 ring-transparent group-data-focused:ring-teal-600 group-data-selected:border-teal-600 sm:size-4 dark:border-white/20 dark:bg-zinc-900 dark:group-data-focused:ring-teal-400 dark:group-data-selected:border-teal-400">
          <span className="size-2 rounded-full bg-teal-600 opacity-0 group-data-selected:opacity-100 dark:bg-teal-400" />
        </span>
        Express
      </Radio>
    </RadioGroup>
  );
}

Plan cards

Turn each radio into a full-width card with supporting details and price.

Choose a plan
radio.cards.tsxtsx
import { Radio, RadioGroup } from "@comp0/react";

const plans = [
  {
    value: "starter",
    name: "Starter",
    description: "For personal projects and prototypes.",
    price: "$0",
  },
  {
    value: "pro",
    name: "Pro",
    description: "For teams shipping multiple products.",
    price: "$12",
  },
  {
    value: "business",
    name: "Business",
    description: "For organizations that need advanced controls.",
    price: "$49",
  },
];

export function Example() {
  return (
    <RadioGroup className="m-0 w-full max-w-md border-0 p-0" defaultValue="pro" name="plan">
      <legend className="mb-3 text-base font-medium text-zinc-950 sm:text-sm dark:text-white">
        Choose a plan
      </legend>
      <div className="grid gap-3">
        {plans.map((plan) => (
          <Radio
            key={plan.value}
            value={plan.value}
            className="group grid cursor-pointer grid-cols-[auto_1fr_auto] items-start gap-3 rounded-xl border border-zinc-950/10 bg-white p-4 text-left text-zinc-800 outline-2 outline-offset-2 outline-transparent transition hover:border-zinc-950/20 data-focused:outline-teal-600 data-selected:border-teal-600 data-selected:bg-teal-50 dark:border-white/10 dark:bg-zinc-900 dark:text-zinc-100 dark:hover:border-white/20 dark:data-focused:outline-teal-400 dark:data-selected:border-teal-400 dark:data-selected:bg-teal-950/40"
          >
            <span
              aria-hidden="true"
              className="mt-0.5 grid size-5 shrink-0 place-items-center rounded-full border border-zinc-950/20 bg-white group-data-selected:border-teal-600 dark:border-white/20 dark:bg-zinc-900 dark:group-data-selected:border-teal-400"
            >
              <span className="size-2 rounded-full bg-teal-600 opacity-0 group-data-selected:opacity-100 dark:bg-teal-400" />
            </span>
            <span className="grid gap-1">
              <span className="font-medium text-zinc-950 dark:text-white">{plan.name}</span>
              <span className="text-base text-zinc-600 sm:text-sm dark:text-zinc-400">
                {plan.description}
              </span>
            </span>
            <span className="font-medium text-zinc-950 dark:text-white">{plan.price}</span>
          </Radio>
        ))}
      </div>
    </RadioGroup>
  );
}

Anatomy

Dashed frames are invisible state providers; shaded shapes own real DOM. Numbered pins match the list below.

A wireframe sketch of the assembled component. Each numbered marker matches a part in the list that follows.

  1. RadioGroup

    Selected-value provider; no DOM by default. Does not add a DOM element.

  2. Radio

    Labelled native radio option. Owns a DOM element.

Step by step

  1. 1

    Add the main part

    Start RadioGroup with a name.

  2. 2

    Add the supporting parts

    Add one Radio per choice, each with a different value.

  3. 3

    Make the behavior clear

    Use defaultValue to show the starting choice.

    Exampletsx
    <RadioGroup name="plan" defaultValue="pro"><Radio value="pro">Pro</Radio></RadioGroup>

Keyboard

Moves to the next radio.
Moves to the previous radio.
Space
Selects the focused radio.

Forms and accessibility

The selected radio submits RadioGroup.name and its value.

Accessibility checklist

  • Name the group with a Legend or aria-label.
  • Use radio only when one choice is allowed.
  • Keep every option label easy to click and read.

API reference

Importtsx
import { Radio, RadioGroup } from "@comp0/react";

RadioGroup

Context only

Selected-value provider; no DOM by default.

PropTypeDescription
valuestringControlled choice.
defaultValuestringInitial choice.
onChange(value: string) => voidReceives the next choice.
namestringShared submission name for the group.
requiredbooleanRequires one radio in the group to be selected.

Radio

DOM element

Labelled native radio option.

PropTypeDescription
valuestringThis option’s value.
checked / defaultCheckedbooleanControlled or initial standalone state.
disabledbooleanDisables the option.

Style hooks

Attributes that appear while a state is true. Target them with Tailwind data variants such as data-open:bg-zinc-100, or with any CSS selector.

[data-selected]

on Radio

This radio is selected.
[data-disabled]

on Radio

This radio is disabled.

Keep exploring