Radio
Labeled radio control for mutually exclusive choices, built on RadioPrimitive.
Overview
Radio is the shared labeled radio control used for mutually exclusive choices.
It wraps RadioPrimitive with:
- optional label text
- left or right label placement
- clickable label behavior
Unlike a native HTML radio group, each Radio is a controlled boolean item. The parent is responsible for group state and ensuring only the intended option is checked.
Basic Usage
<div className="space-y-2">
<Radio
label="Save over current version"
checked={!saveAsNewVersion}
onChange={() => setSaveAsNewVersion(false)}
/>
<Radio
label="Save as new version"
checked={saveAsNewVersion}
onChange={() => setSaveAsNewVersion(true)}
/>
</div>This is the pattern used in PortalEditorLayout.tsx.
Label Placement
Use labelSide="right" when the option layout reads better with the control before the text.
<Radio
label="Send notifications"
labelSide="right"
checked={sendNotifications}
onChange={setSendNotifications}
/>Props
| Prop | Type | Default | Description |
|---|---|---|---|
checked | boolean | — | Whether this option is selected. |
onChange | (checked: boolean) => void | — | Called when the radio is toggled. |
label | string | — | Optional label text. |
labelSide | 'left' | 'right' | 'left' | Which side the label appears on. |
labelClickable | boolean | true | Allows clicking the label to toggle the radio. |
size | 'sm' | 'md' | 'md' | Control size. |
disabled | boolean | false | Disables interaction. |
Radio Primitive
RadioPrimitive is the low-level circle control behind Radio.
Use it directly only when you need a custom radio-group layout that should not use the standard Radio label wrapper.
<div className="flex items-center gap-2">
<RadioPrimitive
checked={value === 'draft'}
onChange={() => setValue('draft')}
aria-label="Draft"
/>
<span>Draft</span>
</div>Radio composes:
Labelfor the visible textRadioPrimitivefor the actual circular control
It passes label through as aria-label to the primitive, so unlabeled custom usage should still provide an accessible label another way.
Primitive props:
| Prop | Type | Default | Description |
|---|---|---|---|
checked | boolean | — | Whether the radio is selected. |
onChange | (checked: boolean) => void | — | Called when the radio is toggled. |
aria-label | string | — | Accessible label for the control. |
size | 'sm' | 'md' | 'md' | Control size. |
disabled | boolean | false | Disables interaction. |
Brand Dashboard Usage
Representative usages:
| Pattern | Path | Notes |
|---|---|---|
| Explicit version choice | fsai/apps/brand-dashboard/src/pages/PortalEditor/ApplicantPortalEditor/PortalEditorLayout/PortalEditorLayout.tsx | Two radios model a mutually exclusive save choice. |
| Deal or fee options | fsai/apps/brand-dashboard/src/modules/deals/components/DealOverviewFees.tsx | Radios used for exclusive fee configuration choices. |
| Settings choice in a modal | fsai/apps/brand-dashboard/src/modules/library/components/DeleteCollectionConfirmModal/DeleteCollectionConfirmModal.tsx | Radios used for modal-level choice between behaviors. |
| Quiz configuration | fsai/apps/brand-dashboard/src/pages/CourseBuilder/QuizBuilder/QuizSettings.tsx | Mutually exclusive settings selection. |
Important Cautions
Radiois not a native<input type="radio">.- There is no built-in
namegrouping or native form-post behavior. - The parent component must own the selected value and keep the group mutually exclusive.
RadioPrimitiverenders a custom element withrole="radio"andaria-checked, but it still does not provide native group semantics by itself.- For explicit yes/no controls, check whether
BooleanRadioalready matches the desired UI before composing a pair of radios yourself.
Guidelines
- Do use
Radiowhen the user must pick exactly one option from a small set - Do keep group state in the parent component
- Do use
RadioPrimitiveonly for custom option layouts - Do use
ToggleorCheckboxinstead when the control is really a boolean on/off state - Don't assume
Radiobehaves like a native radio input group - Don't use a single
Radioby itself for a normal boolean field