Fluxon.Components.Checkbox
(Fluxon v3.0.0)
Checkbox inputs for boolean toggles, opt-in confirmations, and multi-select option lists.
Renders both single checkboxes and grouped checkbox lists with built-in label, sublabel,
description, and error rendering. Integrates directly with Phoenix forms via the field
attribute, and ships a card variant for richer selection surfaces such as plan pickers,
feature toggles, or day-of-week selectors.
Choosing between checkbox and related inputs
| Component | Best suited for |
|---|---|
checkbox/1 | Single boolean opt-in (terms, marketing, "remember me"). |
checkbox_group/1 | Multi-select from a known set of options into a single form field. |
Fluxon.Components.Radio | Single-selection from a mutually exclusive set of options. |
Fluxon.Components.Switch | On/off toggle for an immediately applied setting. |
Usage
The simplest form takes a name and a label:
<.checkbox name="terms" label="I agree to the terms and conditions" />By default the checkbox submits "true" when checked and "false" when unchecked. Both
values are always present in the form payload because a hidden input mirrors the field name
with the unchecked value:
# checked
%{"terms" => "true"}
# unchecked (hidden input still submits)
%{"terms" => "false"}Override the wire values with checked_value and unchecked_value to match the schema
the server expects:
<.checkbox name="active" label="Active" checked_value="1" unchecked_value="0" />Add sublabel for a short inline qualifier and description for longer explanatory copy
rendered beneath the label:
<.checkbox
name="notifications"
label="Enable notifications"
sublabel="Recommended"
description="We'll send you important updates about your account status and security."
/>Form Integration
The component offers two ways to wire form data: the field attribute for Phoenix form
bindings, or the name attribute for standalone controls.
Using with Phoenix forms (recommended)
Pass a Phoenix.HTML.FormField to field and the component derives id, name, the
current value, and any validation errors automatically. Errors are only shown after the
field has been used (Phoenix.Component.used_input?/1), matching the convention used
across the rest of Fluxon's form inputs.
<.form :let={f} for={@form} phx-change="validate" phx-submit="save">
<.checkbox
field={f[:marketing_emails]}
label="Marketing emails"
sublabel="Receive updates about new features and promotions"
/>
<.checkbox
field={f[:terms_accepted]}
label="Terms and Conditions"
description="I agree to the terms of service and privacy policy"
/>
<.checkbox
field={f[:active]}
label="Active user"
checked_value="1"
unchecked_value="0"
/>
</.form>Using standalone checkboxes
When not bound to a form, supply name, manage checked yourself, and pass any error
messages explicitly through errors:
<.checkbox
name="show_archived"
checked={@show_archived}
label="Show archived items"
/>
<.checkbox
name="user[preferences][dark_mode]"
checked={@user_preferences.dark_mode}
errors={@errors["dark_mode"]}
label="Dark mode"
sublabel="Use dark theme"
/>When to use each approach
Use field when working with changesets, validation messages, nested associations, or
any CRUD form. Use name for standalone toggles such as table filters, page-level
preferences, or controls that drive phx-change handlers without a backing schema.
Checked State Resolution
How the rendered checked attribute is computed depends on which inputs you provide:
| Inputs supplied | Resolution |
|---|---|
checked (boolean) | Used verbatim. Wins over value comparisons. |
field and no checked | field.value compared (as escaped HTML) against checked_value. |
value and no checked | value compared against checked_value. |
Neither value nor checked | Renders unchecked. |
The comparison uses Phoenix.HTML.html_escape/1 on both sides, so integers and strings
with the same string representation match (for example value={1} against
checked_value="1").
Variants
The variant attribute controls the surrounding chrome:
nil(default): Standard checkbox aligned next to its label, sublabel, and description. Use for inline opt-ins, settings rows, and the bulk of form fields."card": The entire row becomes a clickable card that highlights when checked. Use when you want each option to feel like a tappable surface, for example in plan selectors, feature pickers, or onboarding flows.
In the card variant, control positions the checkbox input within the card:
"left": Checkbox on the leading edge, label content trailing. Default-feeling layout for option lists."right": Checkbox on the trailing edge, label content leading. Useful when the label carries the primary visual weight (titles, icons, prices).
When neither value is set in the card variant, the checkbox input is visually hidden
(sr-only) and the entire card surface communicates the checked state. This is the
pattern used by the weekday selector example below.
Examples
Confirmation checkbox bound to a changeset:
<.form :let={f} for={@form} phx-submit="register">
<.checkbox
field={f[:terms_accepted]}
label="I accept the Terms of Service"
description="You must agree before creating an account."
/>
<.button type="submit">Create account</.button>
</.form>Filter toggle wired directly to a LiveView event:
<.checkbox
name="show_archived"
checked={@show_archived}
label="Show archived"
phx-click="toggle_archived"
/>Plan selector using the card variant with custom inner content:
<.checkbox name="plan" variant="card" checked_value="pro" unchecked_value="free">
<div>
<h3 class="font-semibold">Pro plan</h3>
<p class="text-sm text-foreground-muted">$19 / month, billed annually.</p>
</div>
</.checkbox>Group of notification channels bound to a form field:
<.checkbox_group
field={f[:notification_channels]}
label="Notifications"
description="Choose how you want to be notified."
>
<:checkbox value="email" label="Email" sublabel="Daily digest" />
<:checkbox value="push" label="Push" sublabel="Real-time alerts" />
<:checkbox value="sms" label="SMS" sublabel="Critical events only" disabled />
</.checkbox_group>Day-of-week picker rendered as compact cards:
<.checkbox_group
field={f[:weekdays]}
variant="card"
label="Working days"
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 has-checked:bg-zinc-800 text-zinc-700 has-checked:text-white size-10"
>
<span class="text-sm font-medium">{label}</span>
</:checkbox>
</.checkbox_group>Checkbox Groups
checkbox_group/1 maps a list of :checkbox slot entries to a single form field that
collects every selected value. The component appends [] to the supplied name so the
server receives a list, and renders an additional hidden input with an empty value so the
field is always present in the form payload.
<.checkbox_group
name="preferences"
label="Notification preferences"
description="Choose when you want to receive notifications"
>
<:checkbox value="email" label="Email notifications" />
<:checkbox value="push" label="Push notifications" />
<:checkbox value="sms" label="SMS notifications" />
</.checkbox_group>Each :checkbox slot accepts its own value, label, sublabel, description,
disabled, checked, and class, plus an optional inner block (used by the card variant
to swap the default label structure for custom content). The slot's checked is honored
in addition to membership in the group's bound value list, so you can pre-check
individual options independent of the form state.
Empty values in checkbox groups
Browsers only submit values for boxes that are actually checked. To keep the field present in form data even when nothing is selected, the group renders a hidden input with an empty string value:
<input type="hidden" name="preferences[]" value="" />Submissions therefore look like one of:
# nothing selected
%{"preferences" => [""]}
# one or more selected
%{"preferences" => ["", "email", "push"]}Filter the empty string in your handler before treating the value as the user's choice:
selected = Enum.reject(params["preferences"] || [], &(&1 == ""))Disabled State
Setting disabled on checkbox/1 or checkbox_group/1 disables the underlying input(s)
and propagates the disabled style to label, sublabel, and description. On the single
checkbox, the hidden mirror input is also marked disabled so the field is omitted from
the submitted payload, matching native HTML form behavior.
Group items can be disabled individually by setting disabled on a :checkbox slot,
leaving the rest of the group interactive.
Form Attribute
Both components forward a form attribute through :rest to the rendered inputs and to
the hidden mirror input(s). Use it when the checkbox lives outside the <form> element
it belongs to:
<.checkbox name="agree" label="I agree" form="signup-form" />
Summary
Components
Renders a single checkbox input alongside its label, sublabel, description, and errors.
Renders a group of checkboxes that collect multiple selections into a single form field.
Components
Renders a single checkbox input alongside its label, sublabel, description, and errors.
Use for boolean opt-ins (terms acceptance, marketing consent, "remember me") and any
field that maps to a single value. The component renders a hidden mirror input with the
unchecked value so the field is always present in submitted form data, even when the box
is unchecked. Pair with field for Phoenix form binding, or with name for standalone
control such as filter toggles or LiveView event handlers.
Attributes
id(:any) - Identifier applied to the checkbox<input>and referenced by the<label>for pointer association. When omitted, an ID is generated automatically; afieldbinding will reusefield.idinstead.Defaults to
nil.name(:any) - Form field name applied to both the checkbox input and its hidden mirror input. Required whenfieldis not provided.checked(:boolean) - Forces the checked state when supplied, taking precedence over anyvalueversuschecked_valuecomparison. Omit to let the component derive the state fromvalue(or fromfield.valuewhen bound to a form).value(:any) - Current value associated with the field. The component compares it againstchecked_value(usingPhoenix.HTML.html_escape/1on both sides) to decide whether to render checked. Provided automatically whenfieldis set.checked_value(:any) - Value submitted when the checkbox is checked. Defaults to"true". Override to match the underlying schema, for example"1"for boolean integer columns or domain-specific strings such as"yes"or"accepted".<.checkbox name="active" checked_value="1" unchecked_value="0" /> <.checkbox name="agree" checked_value="yes" unchecked_value="no" />Defaults to
"true".unchecked_value(:any) - Value submitted via the hidden mirror input when the checkbox is unchecked. Defaults to"false". Browsers do not submit unchecked checkboxes, so this hidden input is what keeps the field present in the payload.Defaults to
"false".errors(:list) - Error messages to render beneath the checkbox. Populated automatically from validation output whenfieldis supplied and the field has been used; pass explicitly when managing state outside of Phoenix forms.Defaults to
[].label(:string) - Primary label rendered next to the checkbox. Omit to render only the input, for example when the surrounding markup provides its own labeling.Defaults to
nil.sublabel(:string) - Short inline qualifier rendered beside the main label (for example "Optional", "Recommended"). Has no effect whenlabelisnil.Defaults to
nil.description(:string) - Longer explanatory copy rendered beneath the label. Use to provide context the label alone cannot carry.Defaults to
nil.class(:any) - Extra classes merged onto the rendered surface. In the standard variant they apply to the checkbox<input>; in the card variant they apply to the wrapping<label>so you can restyle the card itself (background, radius, sizing).Defaults to
nil.field(Phoenix.HTML.FormField) - Phoenix form field to bind to. When provided, the component derivesid,name,value, anderrorsfrom the field.variant(:string) - Visual chrome around the checkbox.nil(default): Standard checkbox aligned next to its label and description. Use for inline opt-ins and most form fields."card": The whole row becomes a clickable card surface that highlights when checked. Use for richer selections such as plan pickers or feature toggles.
Defaults to
nil.control(:string) - Position of the checkbox input within the card. Only meaningful whenvariant="card"."left": Checkbox leads, label content trails."right": Checkbox trails, label content leads.
When omitted in the card variant, the checkbox input is visually hidden and the card surface communicates the checked state on its own.
Must be one of "left", or "right".
- Global attributes are accepted. Additional HTML attributes forwarded to the checkbox
<input>.disabledpropagates to the hidden mirror input as well, so a disabled checkbox is excluded from the submitted payload.formis forwarded to both inputs, allowing the checkbox to live outside the<form>element it belongs to. Supports all globals plus:["disabled", "form"].
Slots
inner_block- Custom content rendered inside the card surface in place of the default label/sublabel/description structure. Only meaningful whenvariant="card"; ignored by the standard variant.
Renders a group of checkboxes that collect multiple selections into a single form field.
Use this when a single field on the form (a list column, an array of associations, a
multi-select preference) should be populated from several related options. Each option is
declared via a :checkbox slot entry; the component takes care of suffixing the field
name with [], keeping the field present in the payload via a hidden empty input, and
rendering label, description, and error messages around the group.
Pair with the field attribute for automatic Phoenix form binding, or use name plus
value for standalone control.
Attributes
id(:any) - Identifier applied to the group. Each:checkboxslot input derives itsidby appending its index (for example"id-0"). When omitted, an ID is generated automatically; afieldbinding will reusefield.idinstead.Defaults to
nil.name(:string) - Form field name for the group. The component appends[]so the server receives a list. Required whenfieldis not provided.value(:any) - List of currently selected values. Each:checkboxslot whosevalueis a member of this list renders pre-checked. Defaults to[]when omitted, or tofield.value(or[]ifnil) when bound viafield.label(:string) - Heading rendered above the checkboxes viaFluxon.Components.Form.label/1. Omit to render the group without a heading, for example when the surrounding layout already provides one.Defaults to
nil.sublabel(:string) - Short qualifier rendered alongside the mainlabel(for example "Optional", "Recommended"). Has no effect whenlabelisnil.Defaults to
nil.description(:string) - Longer explanatory copy rendered beneath the label. Use to clarify what the group is for or how the selection will be used.Defaults to
nil.errors(:list) - Error messages to render beneath the group. Populated automatically from validation output whenfieldis supplied and the field has been used; pass explicitly when managing state outside of Phoenix forms.Defaults to
[].class(:any) - Extra classes applied to the inner container that holds the checkboxes. Use this to switch the layout from the default stacked column to, for example, a horizontal row ("flex gap-x-2") or a multi-column grid.Defaults to
nil.field(Phoenix.HTML.FormField) - Phoenix form field to bind to. When provided, the component derivesid,name,value, anderrorsfrom the field.disabled(:boolean) - Disables every checkbox in the group whentrue. Individual options can still be disabled via the:checkboxslot's owndisabledattribute regardless of this value.Defaults to
false.variant(:string) - Visual chrome wrapping each option.nil(default): Standard stacked checkboxes with label, sublabel, and description. Use for typical multi-select form fields."card": Each option becomes a clickable card surface that highlights when checked. Use for richer pickers (feature toggles, day selectors, plan options).
Defaults to
nil.control(:string) - Position of the checkbox input within each card. Only meaningful whenvariant="card"."left": Checkbox leads, label content trails. Default-feeling for option lists."right": Checkbox trails, label content leads. Use when the label carries the primary visual weight.
When omitted in the card variant, the checkbox input is visually hidden and the entire card surface communicates the checked state.
Must be one of "left", or "right".
- Global attributes are accepted. Additional HTML attributes forwarded to every checkbox
<input>in the group. Theformattribute is also forwarded to the hidden empty-value input so the group stays associated with the correct form when rendered outside of it. Supports all globals plus:["disabled", "form"].
Slots
checkbox(required) - One entry per option in the group. The slot's inner block, when present, is rendered inside the card surface in place of the default label structure (only meaningful withvariant="card").Accepts attributes:value(:any) (required) - Value submitted when this option is checked, and used to compute initial state.label(:string) - Primary label rendered next to (or inside) the option.sublabel(:string) - Short inline qualifier rendered beside the option's label.description(:string) - Longer explanatory copy rendered beneath the option's label.disabled(:boolean) - Disables this option independently of the group-leveldisabledflag.class(:any) - Extra classes applied to the option. In the standard variant they are merged onto the checkbox<input>; in the card variant they are merged onto the card<label>.checked(:boolean) - Forces this option's initial checked state regardless of groupvaluemembership. Use to seed pre-checked options when not driven by form data.