Radio group
A visible set of mutually exclusive choices with descriptions, keyboard selection and form semantics.
On this page
Choose how much detail the reader needs.
Summary format selected.
Code
import { RadioGroup } from "@bosunkit/components/radio-group";
import { useState, type Dispatch, type ReactElement, type SetStateAction } from "react";
export const title: string = "Choose one report format";
export default function Example(): ReactElement {
const [value,setValue]: [string,Dispatch<SetStateAction<string>>] = useState("summary");
return <div><RadioGroup label="Report format" hint="Choose how much detail the reader needs." value={value} onValueChange={setValue}
options={[
{value:"summary",label:"Executive summary",description:"Decisions, outcomes and next priorities."},
{value:"technical",label:"Technical report",description:"Architecture, implementation and verification evidence."},
{value:"slides",label:"Presentation",description:"A narrative for a live discussion."},
{value:"email",label:"Investor email",description:"Available after the recipient list is configured.",disabled:true},
]} /><p role="status">{value === "summary" ? "Summary format selected." : value === "technical" ? "Technical format selected." : "Presentation format selected."}</p></div>;
}
Choosing a presentation
Use a radio group when seeing the available options helps a decision. Use Select for a longer compact list, segmented control for short peer modes, and choice cards when each choice needs an illustration or more space.
The value / onValueChange pair is controlled; defaultValue starts an uncontrolled group. An empty group
can remain unselected until the reader makes a choice. Do not invent a default for a consequential decision.
Layout and states
This is a recorded preference.
Code
import { RadioGroup } from "@bosunkit/components/radio-group";
import type { ReactElement } from "react";
export const title: string = "Horizontal, read-only and invalid choices";
export default function Example(): ReactElement {
const options: readonly {value:string;label:string}[] = [{value:"comfortable",label:"Comfortable"},{value:"compact",label:"Compact"}];
return <div style={{display:"grid",gap:"var(--space-xl)"}}>
<RadioGroup label="Layout density" options={options} orientation="horizontal" defaultValue="comfortable" />
<RadioGroup label="Recorded density" options={options} value="compact" readOnly hint="This is a recorded preference." />
<RadioGroup label="Choose a density" options={options} error="Choose a layout density before continuing." />
</div>;
}
The vertical layout gives descriptions room. Horizontal groups wrap when the container becomes narrow.
Disabled options stay visible and are skipped by keyboard selection. Read-only groups retain their value while
preventing changes. Pass an error to mark and describe an invalid group; individual option descriptions remain
associated with their own controls.
Form integration
Code
import { RadioGroup } from "@bosunkit/components/radio-group";
import { Button } from "@bosunkit/components/button";
import { useState, type Dispatch, type SubmitEvent, type ReactElement, type SetStateAction } from "react";
export const title: string = "Native submission and reset";
export default function Example(): ReactElement {
const [result,setResult]: [string,Dispatch<SetStateAction<string>>] = useState("");
function submit(event: SubmitEvent<HTMLFormElement>): void {
event.preventDefault(); const data: FormData = new FormData(event.currentTarget);
setResult(data.get("format") === "pdf" ? "Example form submitted PDF." : "Example form submitted Markdown.");
}
return <form onSubmit={submit} onReset={(): void => { setResult(""); }}>
<RadioGroup label="Download format" name="format" required defaultValue="pdf" options={[{value:"pdf",label:"PDF"},{value:"markdown",label:"Markdown"}]} />
<div style={{display:"flex",gap:"var(--space-sm)"}}><Button type="submit">Read form values</Button><Button type="reset" variant="ghost">Reset format</Button></div>
<p role="status">{result}</p>
</form>;
}
name submits the selected option's value. required uses native form validation; the caller supplies its own
validation message when intercepting submission. Uncontrolled groups reset to their default when the owning form
resets; controlled groups follow the caller's reset handler. form associates a group with a form elsewhere in
the document. A disabled group contributes no value.
Save only when the caller's workflow requests it. The group does not fetch, persist or confirm an operation.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| label | string | undefined | – | Visible translated group name. |
| options | readonly RadioOption[] | undefined | – | Choices with unique values; exactly one can be selected. |
| children | ReactNode | – | Custom Radio.Root children; use options or children, not both. |
| hint | string | undefined | – | Supporting instructions announced with the group. |
| error | string | undefined | – | Validation message; marks the group invalid. |
| value | string | undefined | – | Controlled selected value. |
| defaultValue | string | undefined | "" | Initial selection when uncontrolled. |
| onValueChange | Primitive.Props<string>["onValueChange"] | – | Reports a requested selection; the caller owns saving. |
| name | string | undefined | – | Submitted field name. |
| form | string | undefined | – | ID of an owning form outside the component's ancestors. |
| required | boolean | undefined | – | Requires a choice before native form submission. |
| disabled | boolean | undefined | – | Makes every option unavailable. |
| readOnly | boolean | undefined | – | Keeps the value focusable and submitted, but prevents changes. |
| orientation | "vertical" | "horizontal" | undefined | "vertical" | Stacked by default; horizontal choices wrap when needed. |
| className | string | undefined | – | Additional group classes. |
| testId | string | undefined | – | Root test ID. |
| ref | Ref<HTMLDivElement> | undefined | – | Radio group element. |
| render | React.ReactElement | ComponentRenderFn<RenderFunctionProps, State> | undefined | – | Allows you to replace the component's HTML element
with a different tag, or compose it with another component.
Accepts a ReactElement or a function that returns the element to render. |
| style | React.CSSProperties | ((state: State) => React.CSSProperties | undefined) | undefined | – | Style applied to the element, or a function that returns a style object based on the component's state. |
| inputRef | React.Ref<HTMLInputElement> | undefined | – | A ref to access the hidden input element. |
Also takes every attribute of Omit<Primitive.Props<string>, "className">.
Related
- Checkbox: independent choices and mixed selection.
- Radio cards: richer explanations of a single choice.
- Segmented radio group: compact peer modes.
- Accessibility: names, keyboard paths and touch targets.