implement
Radio Group

Radio Group

Choose exactly one option from a set.

import { Div, Label } from "@implementjs/core";
import { RadioGroup, RadioGroupItem } from "@/lib/components/ui/radio-group";

function Option(value: string, label: string) {
	return Div(
		{ class: "flex items-center gap-2" },
		RadioGroupItem({ id: value, value }),
		Label({ for: value, class: "text-sm leading-none font-medium" }, label),
	);
}

export default function RadioGroupDemo() {
	return RadioGroup(
		{ value: "comfortable", "aria-label": "Density" },
		Option("default", "Default"),
		Option("comfortable", "Comfortable"),
		Option("compact", "Compact"),
	);
}

A radio group is a set of options where choosing one deselects the rest. RadioGroup is the root and owns the value; each RadioGroupItem is one option, rendered as a Button with role="radio".

import { RadioGroup, RadioGroupItem } from "@implementjs/primitives";

RadioGroup(
	{ "aria-label": "Density" },
	RadioGroupItem({ value: "default" }),
	RadioGroupItem({ value: "comfortable" }),
	RadioGroupItem({ value: "compact" }),
);

Each part accepts optional props and children — pass a props object when you need attributes, or pass children directly. See createComponent. Extra props are forwarded onto the underlying Div or Button.

Value

RadioGroup owns which item is checked. Pass a string to seed it, or a signal holding string | null to control it from outside:

const density = signal<string | null>("comfortable");

RadioGroup(
	{ value: density, "aria-label": "Density" },
	RadioGroupItem({ value: "default" }),
	RadioGroupItem({ value: "comfortable" }),
	RadioGroupItem({ value: "compact" }),
);

density.set("compact"); // checks it from outside

Clicking an item checks it. null means nothing is checked yet; a radio group cannot be uncleared by clicking.

Keyboard and focus

The group is one Tab stop: Tab lands on the checked item, and arrow keys move between the options — moving also checks, the way native radios work. loop (default true) wraps from the last item to the first, and Home and End jump to the ends. orientation (default "vertical") is announced to assistive technology and lands on the data-orientation attributes; all four arrow keys move focus either way.

Disabled

Pass disabled on the group to disable every item, or on one item to disable just it. Both accept a signal, set the native disabled attribute, and add data-disabled for styling. Disabled items are skipped by the arrow keys.

RadioGroup(
	{ "aria-label": "Plan" },
	RadioGroupItem({ value: "free" }),
	RadioGroupItem({ value: "team", disabled: true }),
);

Accessibility

The root sets role="radiogroup" and each item role="radio" with aria-checked. Two things are left to you:

  • A name for the group. Point aria-labelledby at a visible heading's id, or pass aria-label.
  • A label per item. Items render as empty buttons; pair each with a Label whose for matches the item's id, or pass aria-label.

required sets aria-required on the group.

Styling

The root sets data-radio-group-root and items set data-radio-group-item, with data-state as "checked" or "unchecked", plus data-value, data-orientation, and data-disabled:

RadioGroupItem({
	value: "compact",
	class: "size-4 rounded-full border data-[state=checked]:border-primary",
});

Put your own indicator inside the item — a dot, a check, anything — and show it against data-state.

API Reference

RadioGroup

The root. Owns which item is checked and the arrow-key focus movement. Sets role="radiogroup". Renders a Div; extra props are forwarded onto it.

PropTypeDefaultDescription
valueSignal<string | null> | string | nullnullThe checked item. Pass a signal to control it from outside; a string seeds uncontrolled state.
disabledSignal<boolean> | booleanfalseDisables every item in the group.
requiredbooleanfalseSets aria-required on the group.
loopbooleantrueWhether arrow keys wrap from the last item back to the first.
orientation"horizontal" | "vertical""vertical"Announced to assistive technology and set as data-orientation.
Data attributeValue
[data-radio-group-root]Present
[data-orientation]"horizontal" | "vertical"
[data-disabled]Present when disabled

RadioGroupItem

One option. Sets role="radio" and aria-checked; arrowing to it checks it. Give it a look and an indicator. Renders a Button; extra props are forwarded onto it.

PropTypeDefaultDescription
value*stringIdentifies the item. Must be unique within the group.
disabledSignal<boolean> | booleanfalsePrevents checking the item. Sets disabled and data-disabled.
Data attributeValue
[data-radio-group-item]Present
[data-state]"checked" | "unchecked"
[data-value]The item's value
[data-orientation]"horizontal" | "vertical"
[data-disabled]Present when disabled