Skip to main content

Radio

Exclusive selections. Clear visual state. Prevent user errors.

1import { Radio } from '@/components/ui/radio';
2
3const cycles = [
4 {
5 value: 'monthly',
6 title: 'Monthly',
7 detail: '$29 per user, cancel any time',
8 defaultChecked: false,
9 },
10 {
11 value: 'yearly',
12 title: 'Yearly',
13 detail: '$24 per user, two months free',
14 defaultChecked: true,
15 },
16];
17
18export function Default() {
19 return (
20 <div className="flex w-full max-w-sm flex-col gap-2">
21 {cycles.map((cycle) => (
22 <label
23 key={cycle.value}
24 className="border-border bg-card flex cursor-pointer items-center gap-3 rounded-lg border p-3"
25 >
26 <Radio name="billing-cycle" value={cycle.value} defaultChecked={cycle.defaultChecked} />
27 <span className="flex flex-col gap-0.5">
28 <span className="text-sm font-medium">{cycle.title}</span>
29 <span className="text-muted-foreground text-xs">{cycle.detail}</span>
30 </span>
31 </label>
32 ))}
33 </div>
34 );
35}

Installation

pnpm dlx nachui add radio

Anatomy

1import { Radio } from '@/components/ui/radio';
1<Radio id="option-one" name="radio-group" />

Variants

Default

1import { Radio } from '@/components/ui/radio';
2
3const cycles = [
4 {
5 value: 'monthly',
6 title: 'Monthly',
7 detail: '$29 per user, cancel any time',
8 defaultChecked: false,
9 },
10 {
11 value: 'yearly',
12 title: 'Yearly',
13 detail: '$24 per user, two months free',
14 defaultChecked: true,
15 },
16];
17
18export function Default() {
19 return (
20 <div className="flex w-full max-w-sm flex-col gap-2">
21 {cycles.map((cycle) => (
22 <label
23 key={cycle.value}
24 className="border-border bg-card flex cursor-pointer items-center gap-3 rounded-lg border p-3"
25 >
26 <Radio name="billing-cycle" value={cycle.value} defaultChecked={cycle.defaultChecked} />
27 <span className="flex flex-col gap-0.5">
28 <span className="text-sm font-medium">{cycle.title}</span>
29 <span className="text-muted-foreground text-xs">{cycle.detail}</span>
30 </span>
31 </label>
32 ))}
33 </div>
34 );
35}

With Label

Radio component alongside a label for context. The name attribute is used to group the options.

Send activity email

One email per event.
Everything from the last 24 hours, at 8:00.
Monday mornings only.
1import { Label } from '@/components/ui/label';
2import { Radio } from '@/components/ui/radio';
3
4const frequencies = [
5 {
6 id: 'digest-realtime',
7 label: 'Right away',
8 hint: 'One email per event.',
9 defaultChecked: false,
10 },
11 {
12 id: 'digest-daily',
13 label: 'Daily digest',
14 hint: 'Everything from the last 24 hours, at 8:00.',
15 defaultChecked: true,
16 },
17 {
18 id: 'digest-weekly',
19 label: 'Weekly summary',
20 hint: 'Monday mornings only.',
21 defaultChecked: false,
22 },
23];
24
25export function WithLabel() {
26 return (
27 <div className="flex w-full max-w-md flex-col gap-4">
28 <p className="text-sm font-medium">Send activity email</p>
29 {frequencies.map((frequency) => (
30 <div key={frequency.id} className="flex items-start gap-2.5">
31 <Radio
32 id={frequency.id}
33 name="digest-frequency"
34 defaultChecked={frequency.defaultChecked}
35 className="mt-0.5"
36 />
37 <div className="flex flex-col gap-1">
38 <Label htmlFor={frequency.id}>{frequency.label}</Label>
39 <span className="text-muted-foreground text-xs">{frequency.hint}</span>
40 </div>
41 </div>
42 ))}
43 </div>
44 );
45}

Disabled

A disabled state stops interactions.

Data region

Current region, set when the workspace was created.
Moving regions requires a support request.
1import { Label } from '@/components/ui/label';
2import { Radio } from '@/components/ui/radio';
3
4const regions = [
5 {
6 id: 'region-us',
7 label: 'US East, Virginia',
8 hint: 'Current region, set when the workspace was created.',
9 defaultChecked: true,
10 },
11 {
12 id: 'region-eu',
13 label: 'Europe, Frankfurt',
14 hint: 'Moving regions requires a support request.',
15 defaultChecked: false,
16 },
17];
18
19export function Disabled() {
20 return (
21 <div className="flex w-full max-w-md flex-col gap-4">
22 <p className="text-sm font-medium">Data region</p>
23 {regions.map((region) => (
24 <div key={region.id} className="flex items-start gap-2.5">
25 <Radio
26 id={region.id}
27 name="data-region"
28 defaultChecked={region.defaultChecked}
29 className="mt-0.5"
30 disabled
31 />
32 <div className="flex flex-col gap-1">
33 <Label htmlFor={region.id} className="opacity-50">
34 {region.label}
35 </Label>
36 <span className="text-muted-foreground text-xs">{region.hint}</span>
37 </div>
38 </div>
39 ))}
40 </div>
41 );
42}

API Reference

Radio

PropTypeDefaultDescription
onCheckedChange(checked: boolean) => void-Callback when checked state changes
disabledboolean-Disables the radio
namestring-Name of the radio group
Found something to improve?

Notice a bug, typo, or missing detail on this page? Help us make the documentation better by opening a GitHub issue.

Create an Issue
Ctrl+I