Starting Point
v0.38.0

Search documentation

Search for a page and jump to it.

Start typing to search...
Go to Page
escClose

Tailwind CSS Radio Group

Use the radio group component to pick exactly one option from a set of radio buttons, like a plan or a shipping method. For on and off settings, reach for the checkbox or switch.

<div class="field-group w-full max-w-sm gap-3" role="radiogroup" aria-label="Density">
  <div class="field field-horizontal">
    <input type="radio" class="radio" id="r1" name="density" value="default" />
    <label class="label" for="r1">Default</label>
  </div>
  <div class="field field-horizontal">
    <input type="radio" class="radio" id="r2" name="density" value="comfortable" checked />
    <label class="label" for="r2">Comfortable</label>
  </div>
  <div class="field field-horizontal">
    <input type="radio" class="radio" id="r3" name="density" value="compact" />
    <label class="label" for="r3">Compact</label>
  </div>
</div>

Usage

Add the radio class to each <input type="radio"> and give the set a shared name. Lay each option out with its label in a horizontal field, and name the group with a field-set and legend or with role="radiogroup" and an aria-label.

ClassDescription
radioAdd to each <input type="radio">
radio-segmentedAdd to a role="radiogroup" wrapper to show the options as a segmented control
radio-segmented-smAdd to radio-segmented for the smaller size
radio-segmentAdd to each <label> wrapping a radio inside radio-segmented
<div class="field field-horizontal">
  <input type="radio" class="radio" id="option-1" name="my-group" />
  <label class="label" for="option-1">Option one</label>
</div>

Examples

Radio with a description

Give options supporting text with field-content.

<div class="field-group w-full max-w-sm gap-3" role="radiogroup" aria-label="Density">
  <div class="field field-horizontal">
    <input type="radio" class="radio" id="desc-r1" name="desc-density" value="default" />
    <div class="field-content">
      <label class="label" for="desc-r1">Default</label>
      <p class="field-description">Standard spacing for most use cases.</p>
    </div>
  </div>
  <div class="field field-horizontal">
    <input type="radio" class="radio" id="desc-r2" name="desc-density" value="comfortable" checked />
    <div class="field-content">
      <label class="label" for="desc-r2">Comfortable</label>
      <p class="field-description">More space between elements.</p>
    </div>
  </div>
  <div class="field field-horizontal">
    <input type="radio" class="radio" id="desc-r3" name="desc-density" value="compact" />
    <div class="field-content">
      <label class="label" for="desc-r3">Compact</label>
      <p class="field-description">Minimal spacing for dense layouts.</p>
    </div>
  </div>
</div>

Radio choice cards

Wrap each option in a label and the whole card becomes clickable, tinting itself while checked.

<div class="field-group w-full max-w-sm gap-3" role="radiogroup" aria-label="Plan">
  <label class="label" for="plus-plan">
    <div class="field field-horizontal">
      <div class="field-content">
        <span class="field-title">Plus</span>
        <p class="field-description">For individuals and small teams.</p>
      </div>
      <input type="radio" class="radio" id="plus-plan" name="plan" value="plus" checked />
    </div>
  </label>
  <label class="label" for="pro-plan">
    <div class="field field-horizontal">
      <div class="field-content">
        <span class="field-title">Pro</span>
        <p class="field-description">For growing businesses.</p>
      </div>
      <input type="radio" class="radio" id="pro-plan" name="plan" value="pro" />
    </div>
  </label>
  <label class="label" for="enterprise-plan">
    <div class="field field-horizontal">
      <div class="field-content">
        <span class="field-title">Enterprise</span>
        <p class="field-description">For large teams and enterprises.</p>
      </div>
      <input type="radio" class="radio" id="enterprise-plan" name="plan" value="enterprise" />
    </div>
  </label>
</div>

Segmented control

Wrap the radios in a radio-segmented group and each option in a radio-segment label to show the choice as a segmented control. Add radio-segmented-sm for the smaller size, and give an icon-only option an sr-only label.

<div class="flex flex-col items-start gap-6">
  <div class="radio-segmented" role="radiogroup" aria-label="Text size">
    <label class="radio-segment">
      <input type="radio" name="seg-size" value="small" />
      Small
    </label>
    <label class="radio-segment">
      <input type="radio" name="seg-size" value="medium" checked />
      Medium
    </label>
    <label class="radio-segment">
      <input type="radio" name="seg-size" value="large" />
      Large
    </label>
  </div>
  <div class="radio-segmented radio-segmented-sm" role="radiogroup" aria-label="Theme">
    <label class="radio-segment">
      <input type="radio" name="seg-theme" value="system" checked />
      <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="20" height="14" x="2" y="3" rx="2"/><line x1="8" x2="16" y1="21" y2="21"/><line x1="12" x2="12" y1="17" y2="21"/></svg>
      <span class="sr-only">System</span>
    </label>
    <label class="radio-segment">
      <input type="radio" name="seg-theme" value="light" />
      <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4"/><path d="M12 2v2"/><path d="M12 20v2"/><path d="m4.93 4.93 1.41 1.41"/><path d="m17.66 17.66 1.41 1.41"/><path d="M2 12h2"/><path d="M20 12h2"/><path d="m6.34 17.66-1.41 1.41"/><path d="m19.07 4.93-1.41 1.41"/></svg>
      <span class="sr-only">Light</span>
    </label>
    <label class="radio-segment">
      <input type="radio" name="seg-theme" value="dark" />
      <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401"/></svg>
      <span class="sr-only">Dark</span>
    </label>
  </div>
</div>

Radio group in a fieldset

Wrap the group in a field set with a legend so it is announced together.

<fieldset class="field-set w-full max-w-sm">
  <legend class="field-legend field-legend-label">Subscription Plan</legend>
  <p class="field-description">Yearly and lifetime plans offer significant savings.</p>
  <div class="field-group gap-3">
    <div class="field field-horizontal">
      <input type="radio" class="radio" id="plan-monthly" name="sub-plan" checked />
      <label class="label font-normal" for="plan-monthly">Monthly ($9.99/month)</label>
    </div>
    <div class="field field-horizontal">
      <input type="radio" class="radio" id="plan-yearly" name="sub-plan" />
      <label class="label font-normal" for="plan-yearly">Yearly ($99.99/year)</label>
    </div>
    <div class="field field-horizontal">
      <input type="radio" class="radio" id="plan-lifetime" name="sub-plan" />
      <label class="label font-normal" for="plan-lifetime">Lifetime ($299.99)</label>
    </div>
  </div>
</fieldset>

Radio group with nested checkboxes

Nest checkboxes inside a radio's field-content, and fade them with a group-has-* variant while the other option is checked.

<fieldset class="field-set w-full max-w-sm">
  <legend class="field-legend">Attendance</legend>
  <p class="field-description">Choose who can attend your event.</p>
  <div class="group/attendance field-group gap-4" role="radiogroup" aria-label="Attendance">
    <div class="field field-horizontal">
      <input type="radio" class="radio" id="attendance-public" name="attendance" checked />
      <div class="field-content">
        <label class="label" for="attendance-public">Public</label>
        <p class="field-description">Tickets will be available to the general public.</p>
        <div class="field-group mt-4 gap-4 transition-opacity group-has-[#attendance-private:checked]/attendance:pointer-events-none group-has-[#attendance-private:checked]/attendance:opacity-50">
          <div class="field field-horizontal">
            <input type="checkbox" class="checkbox" id="attendance-inperson" checked />
            <div class="field-content">
              <label class="label" for="attendance-inperson">In-person</label>
              <p class="field-description">Attendees will be at the event in person.</p>
            </div>
          </div>
          <div class="field field-horizontal">
            <input type="checkbox" class="checkbox" id="attendance-online" />
            <div class="field-content">
              <label class="label" for="attendance-online">Online</label>
              <p class="field-description">Attendees will only be able to view the event online.</p>
            </div>
          </div>
          <div class="field field-horizontal">
            <input type="checkbox" class="checkbox" id="attendance-recorded" />
            <div class="field-content">
              <label class="label" for="attendance-recorded">Recorded</label>
              <p class="field-description">A recording will be available after the event.</p>
            </div>
          </div>
        </div>
      </div>
    </div>
    <div class="field field-horizontal">
      <input type="radio" class="radio" id="attendance-private" name="attendance" />
      <div class="field-content">
        <label class="label" for="attendance-private">Private</label>
        <p class="field-description">Tickets are not available to the general public.</p>
      </div>
    </div>
  </div>
</fieldset>

Disabled

Add disabled to an option and its label dims with it.

<div class="field-group w-full max-w-sm gap-3" role="radiogroup" aria-label="Options">
  <div class="field field-horizontal">
    <input type="radio" class="radio" id="disabled-1" name="disabled-group" disabled />
    <label class="label font-normal" for="disabled-1">Disabled</label>
  </div>
  <div class="field field-horizontal">
    <input type="radio" class="radio" id="disabled-2" name="disabled-group" checked />
    <label class="label font-normal" for="disabled-2">Option 2</label>
  </div>
  <div class="field field-horizontal">
    <input type="radio" class="radio" id="disabled-3" name="disabled-group" />
    <label class="label font-normal" for="disabled-3">Option 3</label>
  </div>
</div>

Invalid

Set aria-invalid on the radios for the error state, or on a radio-segmented group.

<div class="flex w-full max-w-sm flex-col gap-6">
<fieldset class="field-set w-full max-w-sm">
  <legend class="field-legend field-legend-label">Notification Preferences</legend>
  <p class="field-description">Choose how you want to receive notifications.</p>
  <div class="field-group gap-3">
    <div class="field field-horizontal">
      <input type="radio" class="radio" id="invalid-email" name="invalid-notifications" checked aria-invalid="true" />
      <label class="label font-normal" for="invalid-email">Email only</label>
    </div>
    <div class="field field-horizontal">
      <input type="radio" class="radio" id="invalid-sms" name="invalid-notifications" aria-invalid="true" />
      <label class="label font-normal" for="invalid-sms">SMS only</label>
    </div>
    <div class="field field-horizontal">
      <input type="radio" class="radio" id="invalid-both" name="invalid-notifications" aria-invalid="true" />
      <label class="label font-normal" for="invalid-both">Both Email &amp; SMS</label>
    </div>
  </div>
</fieldset>
<div class="radio-segmented" role="radiogroup" aria-label="Plan" aria-invalid="true">
  <label class="radio-segment">
    <input type="radio" name="invalid-plan" value="free" />
    Free
  </label>
  <label class="radio-segment">
    <input type="radio" name="invalid-plan" value="pro" />
    Pro
  </label>
  <label class="radio-segment">
    <input type="radio" name="invalid-plan" value="team" />
    Team
  </label>
</div>
</div>

RTL

Add dir="rtl" to any parent element, typically <html>, to render right-to-left. See the RTL guide for details.

<div id="radio-group-rtl-demo" dir="rtl" class="w-full max-w-sm">
  <div class="field-group gap-3" role="radiogroup" aria-label="Density">
    <div class="field field-horizontal">
      <input type="radio" class="radio" id="radio-group-rtl-1" name="radio-group-rtl-density" value="default" />
      <div class="field-content">
        <label class="label" for="radio-group-rtl-1" data-i18n="default">افتراضي</label>
        <p class="field-description" data-i18n="defaultDescription">تباعد قياسي لمعظم حالات الاستخدام.</p>
      </div>
    </div>
    <div class="field field-horizontal">
      <input type="radio" class="radio" id="radio-group-rtl-2" name="radio-group-rtl-density" value="comfortable" checked />
      <div class="field-content">
        <label class="label" for="radio-group-rtl-2" data-i18n="comfortable">مريح</label>
        <p class="field-description" data-i18n="comfortableDescription">مساحة أكبر بين العناصر.</p>
      </div>
    </div>
    <div class="field field-horizontal">
      <input type="radio" class="radio" id="radio-group-rtl-3" name="radio-group-rtl-density" value="compact" />
      <div class="field-content">
        <label class="label" for="radio-group-rtl-3" data-i18n="compact">مضغوط</label>
        <p class="field-description" data-i18n="compactDescription">تباعد أدنى للتخطيطات الكثيفة.</p>
      </div>
    </div>
  </div>
</div>
 
<script>
  const translations = {
    ar: { dir: "rtl", default: "افتراضي", defaultDescription: "تباعد قياسي لمعظم حالات الاستخدام.", comfortable: "مريح", comfortableDescription: "مساحة أكبر بين العناصر.", compact: "مضغوط", compactDescription: "تباعد أدنى للتخطيطات الكثيفة." },
    he: { dir: "rtl", default: "ברירת מחדל", defaultDescription: "ריווח סטנדרטי לרוב מקרי השימוש.", comfortable: "נוח", comfortableDescription: "יותר מקום בין האלמנטים.", compact: "קומפקטי", compactDescription: "ריווח מינימלי לפריסות צפופות." },
    en: { dir: "ltr", default: "Default", defaultDescription: "Standard spacing for most use cases.", comfortable: "Comfortable", comfortableDescription: "More space between elements.", compact: "Compact", compactDescription: "Minimal spacing for dense layouts." },
  };
  window.addEventListener("message", (e) => {
    const t = translations[e.data?.lang];
    if (e.data?.type !== "sp-language" || !t) return;
    document.getElementById("radio-group-rtl-demo").dir = t.dir;
    document.querySelectorAll("[data-i18n]").forEach((el) => {
      el.textContent = t[el.dataset.i18n];
    });
  });
</script>