Checkbox

Boolean input with single, group, and card variants.

Basic

.heex
<.checkbox name="terms" label="I agree to the terms and conditions" />

Sublabel and description

Use sublabel for a short inline qualifier and description for longer copy beneath the label.

Recommended

We'll send you important updates about your account status and security.

Optional

Hear about new features, launches, and the occasional sale.

.heex
<div class="flex flex-col gap-y-4 max-w-sm">
  <.checkbox
    name="notifications"
    label="Enable notifications"
    sublabel="Recommended"
    description="We'll send you important updates about your account status and security."
  />
  <.checkbox
    name="marketing"
    label="Marketing emails"
    sublabel="Optional"
    description="Hear about new features, launches, and the occasional sale."
  />
</div>

Pass a custom inner block to the card variant when the description needs <.link> or other components. The block replaces the default label and description layout.

Custom values

Override checked_value and unchecked_value when the schema expects 1/0 or tokens like yes/no.

Submitted as 1 when checked, 0 when unchecked.

.heex
<div class="flex flex-col gap-y-4 max-w-sm">
  <.checkbox
    name="active"
    label="Active user"
    description="Submitted as 1 when checked, 0 when unchecked."
    checked_value="1"
    unchecked_value="0"
  />
  <.checkbox
    name="agree"
    label="I have read the data processing agreement"
    checked_value="yes"
    unchecked_value="no"
  />
</div>

Standalone toggle

Pair name with checked to drive a LiveView event handler without a backing schema.

Includes items closed in the last 90 days
.heex
<.checkbox
  name="show_archived"
  checked
  label="Show archived items"
  sublabel="Includes items closed in the last 90 days"
  phx-click="toggle_archived"
/>

Disabled

Concurrent, fault-tolerant language.

High-performance, compiled language.

.heex
<.form :let={f} for={%{}} as={:disabled} class="flex flex-col gap-y-4 max-w-sm">
  <.checkbox
    field={f[:elixir]}
    checked
    disabled
    label="Elixir"
    value="elixir"
    description="Concurrent, fault-tolerant language."
  />
  <.checkbox
    field={f[:cpp]}
    disabled
    label="C++"
    value="c++"
    description="High-performance, compiled language."
  />
</.form>

Errors

When bound via field, validation errors render beneath the checkbox once the field has been used.

Web framework for Elixir
Web framework for Ruby
React framework for production
.heex
<.form :let={f} for={@form}>
  <.checkbox field={f[:phoenix]} label="Phoenix" sublabel="Web framework for Elixir" />
  <.checkbox field={f[:ruby_on_rails]} label="Ruby on Rails" sublabel="Web framework for Ruby" />
  <.checkbox field={f[:nextjs]} label="Next.js" sublabel="React framework for production" />
</.form>

Group

Collect several selections into a single form field. checkbox_group appends [] to the name and keeps the field present in form data even when nothing is selected.

Pick the languages you reach for first.

.heex
<.form :let={f} for={%{}} as={:group}>
  <.checkbox_group
    field={f[:languages]}
    label="Programming languages"
    description="Pick the languages you reach for first."
  >
    <:checkbox value="elixir" label="Elixir" />
    <:checkbox value="rust" label="Rust" />
    <:checkbox value="typescript" label="TypeScript" />
    <:checkbox value="python" label="Python" />
  </.checkbox_group>
</.form>

Group with per-option detail

Each :checkbox slot can carry its own sublabel, description, and disabled flag.

Choose how you want to be notified.

Daily digest

A summary of activity sent every morning.

Real-time

Delivered to your devices the moment something happens.

Premium only

Text alerts for the most critical events.

.heex
<.form :let={f} for={%{}} as={:channels}>
  <.checkbox_group
    field={f[:notification_channels]}
    label="Notifications"
    description="Choose how you want to be notified."
  >
    <:checkbox
      value="email"
      label="Email"
      sublabel="Daily digest"
      description="A summary of activity sent every morning."
    />
    <:checkbox
      value="push"
      label="Push"
      sublabel="Real-time"
      description="Delivered to your devices the moment something happens."
    />
    <:checkbox
      value="sms"
      label="SMS"
      sublabel="Premium only"
      description="Text alerts for the most critical events."
      disabled
    />
  </.checkbox_group>
</.form>

Card variant

Set variant="card" to turn each row into a tappable surface that highlights when checked.

.heex
<.form :let={f} for={%{}} as={:cards} class="flex flex-col gap-3 w-full max-w-xs">
  <.checkbox
    field={f[:frontend]}
    variant="card"
    label="Frontend"
    sublabel="Web technologies"
    description="HTML, CSS, JavaScript"
    value="frontend"
    checked
  />
  <.checkbox
    field={f[:backend]}
    variant="card"
    label="Backend"
    sublabel="Server technologies"
    description="Elixir, Phoenix, PostgreSQL"
    value="backend"
  />
  <.checkbox
    field={f[:devops]}
    variant="card"
    label="DevOps"
    sublabel="Infrastructure"
    description="Docker, AWS, CI/CD"
    value="devops"
  />
</.form>

Control position

On the card variant, control="left" leads with the checkbox; control="right" trails it so the label carries the visual weight.

.heex
<.form :let={f} for={%{}} as={:control} class="flex flex-col gap-3 w-full max-w-xs">
  <.checkbox
    control="left"
    field={f[:starter]}
    variant="card"
    label="Starter"
    sublabel="$9 / month"
    description="For solo developers and side projects."
    value="starter"
  />
  <.checkbox
    control="right"
    field={f[:team]}
    variant="card"
    label="Team"
    sublabel="$29 / month"
    description="Collaboration features for growing teams."
    value="team"
  />
</.form>

Card group

Card surfaces compose with checkbox_group for compact multi-column pickers.

Select your favorites.

.heex
<div class="w-full max-w-md">
  <.form :let={f} for={%{}} as={:card_group}>
    <.checkbox_group
      label="Programming languages"
      description="Select your favorites."
      field={f[:languages]}
      variant="card"
      class="grid grid-cols-3 gap-2"
    >
      <:checkbox value="elixir" label="Elixir" />
      <:checkbox value="javascript" label="JavaScript" />
      <:checkbox value="python" label="Python" />
      <:checkbox value="ruby" label="Ruby" />
      <:checkbox value="rust" label="Rust" />
      <:checkbox value="go" label="Go" />
    </.checkbox_group>
  </.form>
</div>

Custom card content

Provide an inner block on each :checkbox slot to replace the default label structure with icons, badges, or any layout you need.

.heex
<.checkbox_group field={f[:categories]} variant="card" class="grid grid-cols-3">
  <:checkbox
    :for={
      {label, value, icon} <- [
        {"Web Design", "web_design", "hero-window"},
        {"UI/UX Design", "ui_ux", "hero-pencil-square"},
        {"Desktop", "desktop", "hero-computer-desktop"},
        {"SEO", "seo", "hero-magnifying-glass"},
        {"Artificial Intelligence", "ai", "hero-sparkles"},
        {"Development", "development", "hero-code-bracket-square"}
      ]
    }
    value={value}
    class="flex-1 relative group transition-colors hover:highlight has-checked:hover:highlight has-checked:border-primary has-checked:highlight-strong"
  >
    <.icon
      name="hero-check-circle-solid"
      class="group-has-checked:block hidden absolute top-2 right-2 size-4 text-primary"
    />
    <div class="flex flex-col items-center justify-center w-full gap-2 py-1">
      <.icon
        name={icon}
        class="size-6 text-foreground-softer transition-colors group-has-checked:text-primary"
      />
      <span class="font-medium text-sm text-foreground-soft transition-colors group-has-checked:text-primary">
        {label}
      </span>
    </div>
  </:checkbox>
</.checkbox_group>

Weekday picker

Omit the control attribute on a card so the surface itself reflects the checked state, leaving room for compact icon-only pickers.

The days you are available for new work.

.heex
<.checkbox_group
  label="Working days"
  description="The days you are available for new work."
  name="weekdays"
  value={["mon", "tue", "wed", "thu", "fri"]}
  variant="card"
  class="flex gap-x-2"
>
  <:checkbox
    :for={
      {label, value} <- [
        {"S", "sun"},
        {"M", "mon"},
        {"T", "tue"},
        {"W", "wed"},
        {"T", "thu"},
        {"F", "fri"},
        {"S", "sat"}
      ]
    }
    value={value}
    class="flex items-center justify-center rounded-full size-10 p-0 gap-0 shadow-none cursor-pointer transition-colors text-foreground-soft hover:highlight has-checked:bg-primary has-checked:border-primary has-checked:hover:bg-primary has-checked:text-foreground-primary"
  >
    <span class="text-sm font-medium">{label}</span>
  </:checkbox>
</.checkbox_group>

Tag picker

The same custom-content pattern, but as pill-shaped wrap-and-flow cards for tagging UIs.

Apply any that fit.

.heex
<.checkbox_group label="Tags" description="Apply any that fit." name="tags" variant="card" class="flex flex-wrap gap-2">
  <:checkbox
    :for={label <- ~w(Modern Minimalist Vintage Retro Elegant Classic Contemporary Industrial Rustic Bohemian)}
    value={String.downcase(label)}
    class="group text-sm flex items-center font-medium gap-1 px-2.5 py-1 rounded-full has-checked:highlight-strong has-checked:border-primary has-checked:text-primary"
  >
    <.icon name="hero-check" class="size-4 group-has-checked:block hidden" />
    {label}
  </:checkbox>
</.checkbox_group>