Radio Group
A set of round buttons where only one can be on. Use it for a short list of choices, such as how often to send a digest or when an agent replies.
States
One 16px size with a 40 × 32px hit area. Checked fills with the primary color.
Unchecked
Checked
Disabled
Disabled checked
Invalid
Invalid checked
Labeled radios
Each radio sits inside a Label. The group starts on Weekly and keeps its own state. Arrow keys skip the disabled radio.
Legend, helper, and descriptions
A controlled group named by a visible legend. The legend, helper, and error sit inside the group, and each radio is described by its own text.
When should it reply?
Slack starts this agent from its attached channels.
Replies to a mention, then follows that thread.
Starts a reply for each new message. Applies to all attached channels.
Value: tag
Choice rows
Field's ChoiceGroup draws RadioGroup as bordered rows. Lists stack; columns split into two from 640px.
List layout frequency
Workspace type
Repeat
Outgoing Slack identity
OptionalChoose a published package link
Usage
import { Label } from "@tulipfarm/sprout/label";
import { RadioGroup, RadioGroupItem } from "@tulipfarm/sprout/radio-group";
<RadioGroup defaultValue="weekly" aria-label="Digest frequency">
<Label>
<RadioGroupItem value="daily" />
Daily
</Label>
<Label>
<RadioGroupItem value="weekly" />
Weekly
</Label>
<Label>
<RadioGroupItem value="never" disabled />
Unavailable
</Label>
</RadioGroup>
const [rule, setRule] = useState<"tag" | "every-message">("tag");
<RadioGroup
value={rule}
onValueChange={setRule}
aria-labelledby="reply-legend"
aria-describedby="reply-helper"
aria-required
>
<p id="reply-legend">When should it reply?</p>
<p id="reply-helper">Slack starts this agent from its attached channels.</p>
<Label>
<RadioGroupItem value="tag" aria-describedby="tag-description" />
When mentioned
</Label>
<p id="tag-description">Replies to a mention, then follows that thread.</p>
</RadioGroup>Pass value and onValueChange to control the group, or defaultValue to let it keep its own state. The group may hold a legend, helper, error, and other text beside its radios.
For a labeled set of bordered rows with descriptions, a requirement marker, and an error, use ChoiceGroup type="radio" fromField. It is built from these parts.
Props
There is one look and one size: a 16px circle with a 40 × 32px hit area. A checked radio fills with the primary color and shows a dot.
RadioGroup takes value,defaultValue, onValueChange,disabled, readOnly, required,name, and ARIA props such as aria-label,aria-labelledby, aria-describedby,aria-required, and aria-invalid. It lays out its children in a grid with an 8px gap; pass className to change that.
RadioGroupItem takes a value,disabled, id, andaria-describedby. Set aria-invalid to draw a danger ring; an unchecked invalid radio also gets a danger border. Both parts accept Base UI props and state functions forclassName.
Accessibility
The group has role="radiogroup" and each item hasrole="radio" with aria-checked, plus a hidden input for forms. Tab moves focus to the checked radio. Arrow keys move to the next radio and select it, skipping disabled ones. Space selects the focused radio. A focused radio shows a 2px focus ring.
Name the group with aria-label oraria-labelledby, and name each radio with a wrappingLabel, a Label htmlFor, oraria-label. Color changes take 120ms and the dot scales in over 180ms; both stop when reduced motion is requested. In forced colors mode a checked radio is highlighted.
Theme tokens
Off uses --sprout-border on --sprout-surface, and hover uses --sprout-border-strong. On uses--sprout-primary with a --sprout-on-primarydot, so the opt-in TulipFarm theme tints it green. Invalid uses--sprout-danger, and focus uses --sprout-focus.