Radio

Single-select input with group and card variants.

Basic

.heex
<.radio_group name="system">
  <:radio value="ubuntu" label="Ubuntu" />
  <:radio value="debian" label="Debian" />
  <:radio value="fedora" label="Fedora" />
</.radio_group>

Group header

Set label, sublabel, and description on the group to introduce the choices.

Recommended

Pick the region closest to most of your users.

.heex
<.radio_group
  name="region"
  label="Deployment region"
  sublabel="Recommended"
  description="Pick the region closest to most of your users."
>
  <:radio value="us-east" label="US East (Virginia)" />
  <:radio value="us-west" label="US West (Oregon)" />
  <:radio value="eu-west" label="EU West (Ireland)" />
  <:radio value="ap-south" label="Asia Pacific (Singapore)" />
</.radio_group>

Per-option detail

Each :radio slot accepts its own sublabel and description.

Most popular

Friendly desktop and server distribution with long-term support releases.

Rock solid

Composed entirely of free and open-source software with a conservative release cycle.

Cutting edge

Tracks upstream releases closely, ideal for developers who want the latest tooling.

.heex
<.radio_group name="detailed_system" label="Operating system" class="max-w-md">
  <:radio
    value="ubuntu"
    label="Ubuntu"
    sublabel="Most popular"
    description="Friendly desktop and server distribution with long-term support releases."
  />
  <:radio
    value="debian"
    label="Debian"
    sublabel="Rock solid"
    description="Composed entirely of free and open-source software with a conservative release cycle."
  />
  <:radio
    value="fedora"
    label="Fedora"
    sublabel="Cutting edge"
    description="Tracks upstream releases closely, ideal for developers who want the latest tooling."
  />
</.radio_group>

Horizontal layout

The class attribute styles the option container; apply flex utilities to lay options out in a row.

.heex
<.radio_group name="sort" label="Sort by" class="flex flex-row gap-6">
  <:radio value="newest" label="Newest" />
  <:radio value="oldest" label="Oldest" />
  <:radio value="popular" label="Most popular" />
</.radio_group>

Default selection

Set checked on a slot to mark it selected when the group has no value bound.

Anyone with the link
Members of your workspace
Only you
.heex
<.radio_group name="visibility" label="Visibility">
  <:radio value="public" label="Public" sublabel="Anyone with the link" />
  <:radio value="team" label="Team" sublabel="Members of your workspace" checked />
  <:radio value="private" label="Private" sublabel="Only you" />
</.radio_group>

Disabled

Set disabled on the group to lock every option, or on a single :radio slot to lock just one.

Contact sales
.heex
<div class="flex flex-col gap-y-8 max-w-sm">
  <.radio_group name="tier-all" label="Whole group disabled" value="pro" disabled>
    <:radio value="free" label="Free" />
    <:radio value="pro" label="Pro" />
    <:radio value="team" label="Team" />
  </.radio_group>

  <.radio_group name="tier-one" label="Single option disabled" value="free">
    <:radio value="free" label="Free" />
    <:radio value="pro" label="Pro" />
    <:radio value="enterprise" label="Enterprise" disabled sublabel="Contact sales" />
  </.radio_group>
</div>

Form integration

Pass field to bind a Phoenix.HTML.FormField; the component derives id, name, value, and validation errors from it.

$0 / month
$19 / month
$49 / month
.heex
<.form :let={f} for={@form} phx-change="validate" phx-submit="save">
  <.radio_group field={f[:plan]} label="Subscription">
    <:radio value="free" label="Free" sublabel="$0 / month" />
    <:radio value="pro" label="Pro" sublabel="$19 / month" />
    <:radio value="team" label="Team" sublabel="$49 / month" />
  </.radio_group>
</.form>

Errors

Pass errors to render validation messages beneath the group, or rely on field to forward changeset errors.

User-friendly
Cutting edge
Minimalist
.heex
<.radio_group name="distro" label="Distribution" errors={["Pick a distribution to continue"]}>
  <:radio value="ubuntu" label="Ubuntu" sublabel="User-friendly" />
  <:radio value="fedora" label="Fedora" sublabel="Cutting edge" />
  <:radio value="arch" label="Arch Linux" sublabel="Minimalist" />
</.radio_group>

Card variant

Set variant="card" to render each option as a bordered surface that highlights when selected.

You can change plans at any time.

.heex
<.radio_group
  name="plan"
  value="pro"
  label="Subscription"
  description="You can change plans at any time."
  variant="card"
  control="left"
  class="max-w-sm"
>
  <:radio
    value="free"
    label="Free"
    sublabel="$0 / month"
    description="For tinkering and side projects."
  />
  <:radio
    value="pro"
    label="Pro"
    sublabel="$19 / month"
    description="For solo developers shipping serious work."
  />
  <:radio
    value="team"
    label="Team"
    sublabel="$49 / month"
    description="Collaboration features for growing teams."
  />
</.radio_group>

Card grid

Combine the card variant with a grid class to lay options out in columns.

Select the number of CPUs for this instance.

.heex
<.radio_group
  name="cpu"
  value="4"
  label="CPUs"
  description="Select the number of CPUs for this instance."
  variant="card"
  class="grid grid-cols-3"
>
  <:radio value="2" label="2 CPUs" />
  <:radio value="4" label="4 CPUs" />
  <:radio value="8" label="8 CPUs" />
  <:radio value="16" label="16 CPUs" />
  <:radio value="32" label="32 CPUs" disabled />
  <:radio value="64" label="64 CPUs" disabled />
</.radio_group>

Control position

Set control="left" to place the radio before the label, or control="right" to place it after.

.heex
<div class="grid grid-cols-2 gap-x-4 w-full">
  <.radio_group
    control="left"
    name="frontend"
    value="svelte"
    label="Frontend"
    variant="card"
  >
    <:radio value="react" label="React" description="Component-based UI library" />
    <:radio value="vue" label="Vue" description="Progressive JavaScript framework" />
    <:radio value="svelte" label="Svelte" description="Compile-time reactive framework" />
  </.radio_group>

  <.radio_group
    control="right"
    name="backend"
    value="phoenix"
    label="Backend"
    variant="card"
  >
    <:radio
      value="phoenix"
      label="Phoenix"
      description="Productive Elixir web framework"
      class="*:data-[part=control]:ml-auto"
    />
    <:radio
      value="rails"
      label="Rails"
      description="Ruby's batteries-included framework"
      class="*:data-[part=control]:ml-auto"
    />
    <:radio
      value="django"
      label="Django"
      description="Python's pragmatic web framework"
      class="*:data-[part=control]:ml-auto"
    />
  </.radio_group>
</div>

Custom card content

Pass an inner block to the :radio slot to replace the default label with custom markup like icons or stacked text.

.heex
<.radio_group
  name="hosting"
  value="cloud"
  label="Hosting environment"
  variant="card"
  class="grid grid-cols-3"
>
  <:radio
    :for={
      {label, value, icon} <- [
        {"Cloud", "cloud", "hero-cloud"},
        {"Server", "server", "hero-server"},
        {"Desktop", "desktop", "hero-computer-desktop"}
      ]
    }
    value={value}
    class="flex-1 group has-checked:border-primary has-checked:highlight-strong"
  >
    <div class="flex flex-col items-center justify-center w-full gap-2">
      <.icon name={icon} class="size-6 text-foreground-muted group-has-checked:text-primary" />
      <span class="font-medium text-sm text-foreground group-has-checked:text-primary">
        {label}
      </span>
    </div>
  </:radio>
</.radio_group>

Pill picker

Omit control on the card variant so the entire surface reflects the checked state, with no visible radio dot.

.heex
<.radio_group
  name="t-shirt-size"
  value="m"
  label="T-shirt size"
  variant="card"
  class="flex gap-x-2"
>
  <:radio
    :for={
      {label, value} <- [
        {"XS", "xs"},
        {"S", "s"},
        {"M", "m"},
        {"L", "l"},
        {"XL", "xl"},
        {"2XL", "2xl"}
      ]
    }
    value={value}
    class="flex items-center justify-center rounded-full size-10 text-sm font-medium text-foreground-muted has-checked:bg-primary has-checked:border-primary has-checked:text-foreground-primary"
  >
    {label}
  </:radio>
</.radio_group>