Components

Radio

A control that allows users to select exactly one option from a set of mutually exclusive choices

Installation

npm install @marmoui/ui
yarn add @marmoui/ui
pnpm add @marmoui/ui
bun add @marmoui/ui

Usage

Import the Radio components from the package:

import { RadioGroup, Radio, Label } from '@marmoui/ui';

Examples

Basic

Simple Radio Group

Basic radio group with labels

<RadioGroup defaultValue="option-1" className="space-y-2">
<div className="flex items-center space-x-2">
  <Radio value="option-1" id="option-1" />
  <Label htmlFor="option-1" className="font-normal">Option One</Label>
</div>
<div className="flex items-center space-x-2">
  <Radio value="option-2" id="option-2" />
  <Label htmlFor="option-2" className="font-normal">Option Two</Label>
</div>
<div className="flex items-center space-x-2">
  <Radio value="option-3" id="option-3" />
  <Label htmlFor="option-3" className="font-normal">Option Three</Label>
</div>
</RadioGroup>

Controlled

Controlled State

Radio group with controlled value

function ControlledRadioGroup() {
const [value, setValue] = React.useState('comfortable');

return (
  <div className="space-y-4">
    <RadioGroup value={value} onValueChange={setValue} className="space-y-2">
      <div className="flex items-center space-x-2">
        <Radio value="comfortable" id="comfortable" />
        <Label htmlFor="comfortable" className="font-normal">Comfortable</Label>
      </div>
      <div className="flex items-center space-x-2">
        <Radio value="compact" id="compact" />
        <Label htmlFor="compact" className="font-normal">Compact</Label>
      </div>
      <div className="flex items-center space-x-2">
        <Radio value="dense" id="dense" />
        <Label htmlFor="dense" className="font-normal">Dense</Label>
      </div>
    </RadioGroup>
    <p className="text-sm text-ink-light">
      Selected: <strong>{value}</strong>
    </p>
  </div>
);
}

With Descriptions

Options with Descriptions

Radio buttons with additional context below each label

Basic features for individuals getting started

Advanced features for professionals and small teams

<RadioGroup defaultValue="free" className="space-y-4">
<div className="flex items-start space-x-3">
  <Radio value="free" id="free" className="mt-0.5" />
  <div>
    <Label htmlFor="free" className="font-medium">Free Plan</Label>
    <p className="text-sm text-ink-light">Basic features for individuals getting started</p>
  </div>
</div>
<div className="flex items-start space-x-3">
  <Radio value="pro" id="pro" className="mt-0.5" />
  <div>
    <Label htmlFor="pro" className="font-medium">Pro Plan</Label>
    <p className="text-sm text-ink-light">Advanced features for professionals and small teams</p>
  </div>
</div>
</RadioGroup>

Horizontal Layout

Inline Radio Buttons

Radio group in horizontal layout

<RadioGroup defaultValue="left" className="flex gap-4">
<div className="flex items-center space-x-2">
  <Radio value="left" id="left" />
  <Label htmlFor="left" className="font-normal">Left</Label>
</div>
<div className="flex items-center space-x-2">
  <Radio value="center" id="center" />
  <Label htmlFor="center" className="font-normal">Center</Label>
</div>
<div className="flex items-center space-x-2">
  <Radio value="right" id="right" />
  <Label htmlFor="right" className="font-normal">Right</Label>
</div>
</RadioGroup>

Disabled State

Disabled Options

Some or all options disabled

<RadioGroup defaultValue="option-1" className="space-y-2">
<div className="flex items-center space-x-2">
  <Radio value="option-1" id="option-1" />
  <Label htmlFor="option-1" className="font-normal">Enabled option</Label>
</div>
<div className="flex items-center space-x-2">
  <Radio value="option-2" id="option-2" disabled />
  <Label htmlFor="option-2" className="font-normal">Disabled option</Label>
</div>
<div className="flex items-center space-x-2">
  <Radio value="option-3" id="option-3" />
  <Label htmlFor="option-3" className="font-normal">Another enabled option</Label>
</div>
</RadioGroup>

In Form Context

Form with Radio Group

Radio group in a complete form

function FormExample() {
const [formData, setFormData] = React.useState({
  frequency: 'daily',
  format: 'html',
});

return (
  <form className="w-80 p-6 border border-border rounded-lg space-y-6">
    <h3 className="text-lg font-semibold">Email Preferences</h3>

    <div className="space-y-3">
      <Label className="text-base font-medium">Email Frequency</Label>
      <RadioGroup
        value={formData.frequency}
        onValueChange={(value) => setFormData(prev => ({ ...prev, frequency: value }))}
        className="space-y-2"
      >
        <div className="flex items-center space-x-2">
          <Radio value="daily" id="daily" />
          <Label htmlFor="daily" className="font-normal">Daily digest</Label>
        </div>
        <div className="flex items-center space-x-2">
          <Radio value="weekly" id="weekly" />
          <Label htmlFor="weekly" className="font-normal">Weekly summary</Label>
        </div>
        <div className="flex items-center space-x-2">
          <Radio value="monthly" id="monthly" />
          <Label htmlFor="monthly" className="font-normal">Monthly newsletter</Label>
        </div>
      </RadioGroup>
    </div>

    <div className="space-y-3">
      <Label className="text-base font-medium">Email Format</Label>
      <RadioGroup
        value={formData.format}
        onValueChange={(value) => setFormData(prev => ({ ...prev, format: value }))}
        className="flex gap-4"
      >
        <div className="flex items-center space-x-2">
          <Radio value="html" id="html" />
          <Label htmlFor="html" className="font-normal">HTML</Label>
        </div>
        <div className="flex items-center space-x-2">
          <Radio value="text" id="text" />
          <Label htmlFor="text" className="font-normal">Plain text</Label>
        </div>
      </RadioGroup>
    </div>

    <Button type="submit" className="w-full">Save Preferences</Button>
  </form>
);
}

Radio Card

RadioCard provides a card-style radio selection interface with rich content including icons, labels, descriptions, and addons.

Basic Radio Card

Card Selection

Rich card-style radio options

Perfect for individuals and small projects

For growing teams with advanced needs

<RadioCard defaultValue="starter">
<RadioCardItem
  value="starter"
  label="Starter Plan"
  description="Perfect for individuals and small projects"
/>
<RadioCardItem
  value="pro"
  label="Pro Plan"
  description="For growing teams with advanced needs"
/>
<RadioCardItem
  value="enterprise"
  label="Enterprise"
  description="Custom solutions for large organizations"
/>
</RadioCard>

With Icons

Icon Cards

Radio cards with visual icons

Send via email campaign

Send via text message

<RadioCard defaultValue="email">
<RadioCardItem
  value="email"
  icon={<MdEmail className="size-6 text-primary" />}
  label="Email"
  description="Send via email campaign"
/>
<RadioCardItem
  value="sms"
  icon={<MdSms className="size-6 text-primary" />}
  label="SMS"
  description="Send via text message"
/>
<RadioCardItem
  value="push"
  icon={<MdNotifications className="size-6 text-primary" />}
  label="Push Notification"
  description="Send via mobile app"
/>
</RadioCard>

With Addon Content

Cards with Addon

Additional content like pricing or actions

Pay month-to-month, cancel anytime

$29/mo

Save 20% with annual payment

$279/yr
<RadioCard defaultValue="monthly">
<RadioCardItem
  value="monthly"
  label="Monthly Billing"
  description="Pay month-to-month, cancel anytime"
  addon={<span className="text-lg font-bold">$29/mo</span>}
/>
<RadioCardItem
  value="yearly"
  label="Yearly Billing"
  description="Save 20% with annual payment"
  addon={
    <div className="flex items-center gap-2">
      <span className="text-lg font-bold">$279/yr</span>
      <Badge variant="success" size="sm">Save $69</Badge>
    </div>
  }
/>
</RadioCard>

Best Practices

✓

Use for mutually exclusive choices

Radio buttons are ideal when users must select exactly one option from a set.

✓

Keep options visible

Show all options upfront so users can compare before choosing. Use Select for long lists.

✓

Set a sensible default

Pre-select the most common or recommended option when appropriate.

✓

Use descriptive labels

Labels should clearly indicate what each option does or represents.

✗

Don't use for multiple selections

If users can select multiple options, use Checkbox instead.

✗

Don't use for more than 5-7 options

For longer lists, consider using a Select dropdown instead.

Props

RadioGroup

PropTypeDefaultDescription
valuestring-Controlled value
defaultValuestring-Default value
onValueChange(value: string) => void-Change handler
disabledbooleanfalseDisable all items
requiredbooleanfalseMark as required
namestring-Form field name
orientation'horizontal' | 'vertical''vertical'Layout direction
classNamestring-Additional className

Radio

PropTypeDefaultDescription
valuestring-Item value (required)
disabledbooleanfalseDisable this item
idstring-Element ID for label association
classNamestring-Additional className

RadioCard

Extends RadioGroup with the same props.

RadioCardItem

PropTypeDefaultDescription
valuestring-Item value (required)
labelReactNode-Card title/label
descriptionReactNode-Description text below label
iconReactElement-Icon element to display
addonReactNode-Additional content (e.g., pricing)
indicatorReactNode | null-Custom indicator element
indicatorPlacement'start' | 'end' | 'inside'-Position of indicator
disabledbooleanfalseDisable this item
classNamestring-Additional className

Accessibility

Keyboard Navigation

Radio groups support keyboard navigation. Use arrow keys to move between options, and Space to select.

  • Built on Radix UI RadioGroup with full ARIA support
  • Uses proper role="radiogroup" and role="radio" attributes
  • Arrow keys navigate between options in the group
  • Space key selects the focused option
  • Focus is automatically managed within the group
  • Required state is announced by assistive technology