Summary

Functions

Renders a filterable autocomplete input backed by a hidden form value.

Renders a checkbox control with optional inline label content.

Field wrapper for label, control, description, and errors.

Wraps the main interactive control inside a field.

Helper text shown beneath a field control.

Error or validation message shown beneath a field control.

Wraps field labels so shared spacing and invalid-state styling remain consistent across controls. Inside field/1, provide it via the :label slot when you need richer label content than a single label/1.

Neutral status or informational message shown beneath a field control.

Renders an input with shadcn classes.

Wraps an input and sibling controls (buttons/icons) in a single inline group.

Text/icon/status/action segment used inside input_group/1.

Compact action button for use inside input_group_addon/1.

Styled text segment for use inside input_group_addon/1.

Renders an OTP-style segmented input layout.

Renders a form label.

Renders a native <select> element with shadcn styles.

Renders a number input with increment and decrement controls.

Renders a radio group with native radio inputs.

Renders a custom select with a button trigger and listbox content.

Renders a slider using native range input(s).

Renders a switch control with optional label content.

Renders a textarea with shadcn classes.

Functions

autocomplete(assigns)

Renders a filterable autocomplete input backed by a hidden form value.

This is intended for searching and selecting from a known set of options. Use select/1 when you want a trigger-driven listbox instead of a text input.

## When to use it

Use autocomplete/1 when the person typing should search by label, but the form needs to submit a separate stable value through the hidden input.

Prefer combobox/1 for simpler label-in/label-out filtering where the typed text itself is the selected value and you do not need a separate hidden form field.

For server-backed search, keep the current query in LiveView assigns and update the option list on phx-change or phx-input. The component keeps its hidden value form-friendly while the visible input remains a normal text field that can participate in debounced LiveView events.

## Examples

heex title="Autocomplete" align="full" <.autocomplete id="team-owner" name="owner" value="levi"> <:option value="levi" label="Levi Buzolic" description="Engineering" /> <:option value="mira" label="Mira Chen" description="Design" /> <:option value="sam" label="Sam Hall" description="Operations" /> </.autocomplete>

heex title="Loading state" align="full" vrt <.autocomplete id="repo-search" name="repo" loading={true}> <:option value="cinder" label="cinder_ui" /> </.autocomplete>

heex title="LiveView server search" align="full" vrt <.form for={%{}} phx-change="search-owners"> <.autocomplete id="owner-search" name="owner" value="mira" placeholder="Search teammates..." loading={false} phx-debounce="300" aria-label="Search owners" > <:option value="levi" label="Levi Buzolic" /> <:option value="mira" label="Mira Chen" /> <:empty>No teammates match the current query.</:empty> </.autocomplete> </.form>

### With FormField

  <.autocomplete field={@form[:owner]}>
    <:option value="levi" label="Levi Buzolic" />
    <:option value="mira" label="Mira Chen" />
  </.autocomplete>

### With label

  <.autocomplete field={@form[:owner]} label="Owner">
    <:option value="levi" label="Levi Buzolic" />
  </.autocomplete>

### With explicit errors

  <.autocomplete field={@form[:owner]} label="Owner" errors={["is required"]}>
    <:option value="levi" label="Levi Buzolic" />
  </.autocomplete>

### Inside field composition

  <.field>
    <:label for={@form[:owner].id}>Owner</:label>
    <.autocomplete field={@form[:owner]}>
      <:option value="levi" label="Levi Buzolic" />
    </.autocomplete>
    <:description>Assign a teammate to this project.</:description>
  </.field>

Screenshot

autocomplete/1 screenshot

View live examples and full component docs.

checkbox(assigns)

Renders a checkbox control with optional inline label content.

## Examples

heex title="Basic checkbox" align="full" <.checkbox id="terms" name="terms">Accept terms</.checkbox>

heex title="Checked state" align="full" <.checkbox id="updates" name="updates" checked={true}>Notify me about product updates</.checkbox>

### With FormField

  <.checkbox field={@form[:active]} />

### With label attr (inline)

  <.checkbox field={@form[:active]} label="Active" />

### With inner_block (takes precedence over label attr)

  <.checkbox field={@form[:terms]}>
    I agree to the <a href="/terms">Terms of Service</a>
  </.checkbox>

### With explicit errors

  <.checkbox field={@form[:terms]} errors={["must be accepted"]} />

### Inside field composition

  <.field>
    <:label for={@form[:active].id}>Active</:label>
    <.checkbox field={@form[:active]} />
  </.field>

Screenshot

checkbox/1 screenshot

View live examples and full component docs.

field(assigns)

Field wrapper for label, control, description, and errors.

field/1 remains the simplest composition helper. It automatically wraps the control passed to its inner block with field_control/1, so most usages should pass the form control directly and use the :label, :description, :message, and :error slots for supporting content.

Prefer the shorthand :label slot with for for ordinary field labels. Reach for raw :label slot content, field_label/1, field_control/1, field_description/1, field_message/1, and field_error/1 when you need richer markup or want to compose the pieces outside field/1.

## Examples

heex title="Profile field" align="full" <.field> <:label for="name">Name</:label> <.input id="name" name="name" /> <:description>Shown in your profile.</:description> </.field>

heex title="Validation state" align="full" vrt <.field> <:label for="email">Work email</:label> <.input id="email" name="email" type="email" /> <:description>We'll send deployment alerts here.</:description> <:error>Please use your company domain.</:error> </.field>

heex title="Custom Label Markup" align="full" <.field invalid={true}> <:label> <.field_label> <.label for="workspace-slug">Workspace slug</.label> <span class="text-muted-foreground text-xs">Used in your public workspace URL.</span> </.field_label> </:label> <.input id="workspace-slug" name="workspace[slug]" value="cinder-ui" /> <:error>Slug has already been taken.</:error> </.field>

heex title="Phoenix validation flow" align="full" vrt <.form for={%{}} phx-change="validate" phx-submit="save" class="space-y-6"> <.field invalid={true}> <:label for="owner">Owner</:label> <.autocomplete id="owner" name="owner" value="levi" aria-label="Owner" > <:option value="levi" label="Levi Buzolic" description="Engineering" /> <:option value="mira" label="Mira Chen" description="Design" /> <:empty>No matching teammates.</:empty> </.autocomplete> <:description>Choose the teammate responsible for this workspace.</:description> <:error>Please choose a teammate.</:error> </.field> </.form>

heex title="Date range fields" align="full" <div class="grid gap-4 sm:grid-cols-2"> <.field> <:label for="report_start_date">Start date</:label> <.input id="report_start_date" name="report[start_date]" type="date" value="2026-06-01" /> <:description>Use the first local day to include in the report.</:description> </.field> <.field invalid={true}> <:label for="report_end_date">End date</:label> <.input id="report_end_date" name="report[end_date]" type="date" value="2026-05-31" min="2026-06-01" aria-invalid="true" /> <:error>End date must be on or after the start date.</:error> </.field> </div>

heex title="LiveView date validation" align="full" vrt <.form for={@form} phx-change="validate" phx-submit="save" class="grid gap-6"> <.field> <:label for={@form[:start_date].id}>Start date</:label> <.input field={@form[:start_date]} type="date" required /> <:description>Changes are validated by the LiveView on phx-change.</:description> </.field> <.field invalid={true}> <:label for={@form[:end_date].id}>End date</:label> <.input field={@form[:end_date]} type="date" min="2026-06-01" aria-invalid="true" /> <:error>End date must be on or after the start date.</:error> </.field> </.form>

Screenshot

field/1 screenshot

View live examples and full component docs.

field_control(assigns)

Wraps the main interactive control inside a field.

field/1 already applies this wrapper around its inner block, so you generally do not need to call field_control/1 inside a normal field/1 example. Use it directly when composing a field manually or when you need to attach the invalid-state control styles outside field/1.

## Example

heex title="Field control wrapper" align="full" <.field_control> <.input id="workspace_slug" value="cinder-ui" /> </.field_control>

heex title="Field control with helper text" align="full" <.field> <:label for="billing_email">Billing email</:label> <.input id="billing_email" name="billing[email]" type="email" placeholder="billing@team.com" /> <:description>Invoices and payment reminders go here.</:description> </.field>

Screenshot

field_control/1 screenshot

View live examples and full component docs.

field_description(assigns)

Helper text shown beneath a field control.

In most field/1 usage, prefer the :description slot. Use field_description/1 directly for isolated helper rendering or custom field composition.

## Example

heex title="Field description" align="full" <.field_description>Used in your public workspace URL.</.field_description>

heex title="Field description in context" align="full" <.field> <:label for="workspace_slug">Workspace slug</:label> <.input id="workspace_slug" name="workspace[slug]" value="cinder-ui" /> <:description>Used in your public workspace URL.</:description> </.field>

Screenshot

field_description/1 screenshot

View live examples and full component docs.

field_error(assigns)

Error or validation message shown beneath a field control.

In most field/1 usage, prefer the :error slot. Use field_error/1 directly for isolated helper rendering or custom field composition.

## Example

heex title="Field error" align="full" <.field_error>Please use your company domain.</.field_error>

heex title="Field error in context" align="full" <.field invalid={true}> <:label for="work_email">Work email</:label> <.input id="work_email" name="work_email" type="email" value="hello@gmail.com" /> <:error>Please use your company domain.</:error> </.field>

Screenshot

field_error/1 screenshot

View live examples and full component docs.

field_label(assigns)

Wraps field labels so shared spacing and invalid-state styling remain consistent across controls. Inside field/1, provide it via the :label slot when you need richer label content than a single label/1.

## Example

heex title="Grouped field label" align="full" <.field_label> <.label for="workspace_name">Workspace name</.label> <span class="text-muted-foreground text-xs">Used across the dashboard.</span> </.field_label>

heex title="Field label in context" align="full" <.field> <:label> <.field_label> <.label for="workspace_name">Workspace name</.label> <span class="text-muted-foreground text-xs">Shown in team switchers.</span> </.field_label> </:label> <.input id="workspace_name" name="workspace[name]" value="Cinder UI" /> </.field>

Screenshot

field_label/1 screenshot

View live examples and full component docs.

field_message(assigns)

Neutral status or informational message shown beneath a field control.

In most field/1 usage, prefer the :message slot. Use field_message/1 directly for isolated helper rendering or custom field composition.

## Example

heex title="Field message" align="full" <.field_message>Visible immediately after save.</.field_message>

heex title="Field message in context" align="full" <.field> <:label for="project_name">Project name</:label> <.input id="project_name" name="project[name]" value="Marketing site refresh" /> <:message>Saved automatically a few seconds ago.</:message> </.field>

Screenshot

field_message/1 screenshot

View live examples and full component docs.

input(assigns)

Renders an input with shadcn classes.

## Examples

heex title="Text input" align="full" <.input id="email" type="email" placeholder="name@example.com" />

heex title="With value" align="full" <.input id="username" name="username" value="levi" />

### With FormField

  <.input field={@form[:email]} />

### With label

  <.input field={@form[:email]} label="Email" />

### With explicit errors

  <.input field={@form[:email]} label="Email" errors={["can't be blank"]} />

### Inside field composition

  <.field>
    <:label for={@form[:email].id}>Email</:label>
    <.input field={@form[:email]} />
    <:description>We'll send alerts here.</:description>
  </.field>

Screenshot

input/1 screenshot

View live examples and full component docs.

input_group(assigns)

Wraps an input and sibling controls (buttons/icons) in a single inline group.

Use input_group_addon/1 for static text, icons, status copy, or compact buttons that should visually attach to the grouped controls.

## Examples

heex title="Search with action" align="full" <.input_group> <.input placeholder="Search" /> <.input_group_addon> <.input_group_button variant={:secondary}>Go</.input_group_button> </.input_group_addon> </.input_group>

heex title="Handle input" align="full" <.input_group> <.input placeholder="organization" /> <.input_group_addon> <.input_group_text>@company.com</.input_group_text> </.input_group_addon> </.input_group>

heex title="URL builder" align="full" <.input_group> <.input_group_addon> <.input_group_text>https://</.input_group_text> </.input_group_addon> <.input value="cinder-ui" /> <.input_group_addon> <.input_group_text>.com</.input_group_text> </.input_group_addon> </.input_group>

heex title="Command search" align="full" <.input_group> <.input_group_addon> <.icon name="search" class="size-4" /> </.input_group_addon> <.input placeholder="Search components" /> <.input_group_addon> <.kbd>⌘K</.kbd> </.input_group_addon> </.input_group>

heex title="Loading state" align="full" <.input_group> <.input placeholder="Generating invite link..." disabled /> <.input_group_addon> <.spinner class="size-4" /> <.input_group_text>Syncing</.input_group_text> </.input_group_addon> </.input_group>

heex title="Select + input" align="full" <.input_group> <.native_select name="team-role" value="admin" class="w-32" aria-label="Team role"> <:option value="admin" label="Admin" /> <:option value="editor" label="Editor" /> <:option value="viewer" label="Viewer" /> </.native_select> <.input placeholder="email@example.com" type="email" class="flex-1" /> </.input_group>

heex title="Textarea with footer action" align="full" <.input_group align={:block_end}> <.textarea rows={3} placeholder="Write a comment..." class="min-h-[5.5rem]" /> <.input_group_addon align={:block_end}> <span>0/280</span> <.button size={:sm}>Post</.button> </.input_group_addon> </.input_group>

heex title="Copy URL action" align="full" <.input_group> <.input placeholder="https://example.com" /> <.input_group_addon> <.input_group_button variant={:outline}>Copy</.input_group_button> </.input_group_addon> </.input_group>

heex title="Icon actions" align="full" <.input_group> <.input value="ck_live_************************" readonly /> <.input_group_addon> <.input_group_button size={:icon_xs} aria-label="Reveal key"> <.icon name="eye" /> </.input_group_button> <.input_group_button size={:icon_xs} aria-label="Copy key"> <.icon name="copy" /> </.input_group_button> </.input_group_addon> </.input_group>

Screenshot

input_group/1 screenshot

View live examples and full component docs.

input_group_addon(assigns)

Text/icon/status/action segment used inside input_group/1.

This is useful for prefixes, suffixes, inline status, and small utility content that should attach to the surrounding grouped field.

## Example

heex title="Input group addon" align="full" <.input_group> <.input_group_addon> <.icon name="mail" class="size-4" /> </.input_group_addon> <.input type="email" placeholder="team@example.com" /> </.input_group>

Screenshot

input_group_addon/1 screenshot

View live examples and full component docs.

input_group_button(assigns)

Compact action button for use inside input_group_addon/1.

This is intentionally smaller than the general button/1 API. Use it for short text actions and icon-only utility actions embedded in an input group.

## Example

heex title="Input group button" align="full" <.input_group> <.input placeholder="Search" /> <.input_group_addon> <.input_group_button>Search</.input_group_button> </.input_group_addon> </.input_group>

Screenshot

input_group_button/1 screenshot

View live examples and full component docs.

input_group_text(assigns)

Styled text segment for use inside input_group_addon/1.

Use this for prefixes, suffixes, units, and short status text when the addon also contains icons or buttons.

## Example

heex title="Input group text" align="full" <.input_group> <.input placeholder="Amount" /> <.input_group_addon> <.input_group_text>USD</.input_group_text> </.input_group_addon> </.input_group>

Screenshot

input_group_text/1 screenshot

View live examples and full component docs.

input_otp(assigns)

Renders an OTP-style segmented input layout.

This component renders one input per position and can be wired using standard Phoenix input names such as code[]. The bundled CuiInputOtp hook adds auto-advance, backspace-to-previous, and paste distribution behavior.

## Examples

heex title="Basic OTP input" align="full" <.input_otp name="verification_code[]" length={6} />

heex title="With grouped separators" align="full" <.input_otp name="backup_code[]" length={6} groups={[3, 3]} values={["1", "2", "3", "4", "5", "6"]} />

### With FormField (string value is split into individual cells)

  <.input_otp field={@form[:code]} length={6} />

### With label

  <.input_otp field={@form[:code]} label="Verification code" length={6} />

### With explicit errors

  <.input_otp field={@form[:code]} label="Verification code" errors={["is invalid"]} length={6} />

### Inside field composition

  <.field>
    <:label for={@form[:code].id}>Verification code</:label>
    <.input_otp field={@form[:code]} length={6} />
    <:description>Enter the 6-digit code from your email.</:description>
  </.field>

Screenshot

input_otp/1 screenshot

View live examples and full component docs.

label(assigns)

Renders a form label.

## Examples

  <.label for="email">Email</.label>

  <.label for="project_name">
    Project name
    <span class="text-destructive">*</span>
  </.label>

Screenshot

label/1 screenshot

View live examples and full component docs.

native_select(assigns)

Renders a native <select> element with shadcn styles.

Use this when you want platform-native select behavior rather than the custom listbox UI from select/1.

## Examples

heex title="Native select" align="full" <.native_select name="framework" value="phoenix"> <:option value="phoenix" label="Phoenix" /> <:option value="rails" label="Rails" /> <:option value="laravel" label="Laravel" /> </.native_select>

heex title="With placeholder" align="full" <.native_select name="assignee" placeholder="Assign a teammate"> <:option value="levi" label="Levi" /> <:option value="juz" label="Justin" /> </.native_select>

### With FormField (using options attr)

  <.native_select field={@form[:role]} options={[{"Admin", "admin"}, {"Member", "member"}]} />

### With FormField (using option slots)

  <.native_select field={@form[:role]}>
    <:option value="admin" label="Admin" />
    <:option value="member" label="Member" />
  </.native_select>

### With label

  <.native_select field={@form[:role]} label="Role" options={[{"Admin", "admin"}]} />

### With explicit errors

  <.native_select field={@form[:role]} label="Role" errors={["is required"]} options={[{"Admin", "admin"}]} />

### Inside field composition

  <.field>
    <:label for={@form[:role].id}>Role</:label>
    <.native_select field={@form[:role]} options={[{"Admin", "admin"}]} />
    <:description>Choose your access level.</:description>
  </.field>

Screenshot

native_select/1 screenshot

View live examples and full component docs.

number_field(assigns)

Renders a number input with increment and decrement controls.

Keyboard interaction comes from the native type="number" input, so arrow keys, min/max constraints, and step behavior stay browser-native while the buttons provide a touch-friendly affordance.

## Examples

heex title="Basic number field" align="full" <.number_field id="seat-count" name="seats" value={3} min={1} max={10} />

heex title="Fractional step" align="full" <.number_field id="discount" name="discount" value={1.5} min={0} max={5} step={0.5} />

### With FormField

  <.number_field field={@form[:quantity]} />

### With label

  <.number_field field={@form[:quantity]} label="Quantity" />

### With explicit errors

  <.number_field field={@form[:quantity]} label="Quantity" errors={["must be positive"]} />

### Inside field composition

  <.field>
    <:label for={@form[:quantity].id}>Quantity</:label>
    <.number_field field={@form[:quantity]} />
    <:description>Enter a positive number.</:description>
  </.field>

Screenshot

number_field/1 screenshot

View live examples and full component docs.

radio_group(assigns)

Renders a radio group with native radio inputs.

## Examples

heex title="Basic radio group" align="full" <.radio_group name="plan" value="pro"> <:option value="free" label="Free" /> <:option value="pro" label="Pro" /> </.radio_group>

heex title="With disabled option" align="full" <.radio_group name="region" value="us"> <:option value="us" label="United States" /> <:option value="eu" label="Europe" disabled={true} /> </.radio_group>

### With FormField

  <.radio_group field={@form[:plan]}>
    <:option value="free" label="Free" />
    <:option value="pro" label="Pro" />
  </.radio_group>

### With label (renders as fieldset/legend, not label/for)

  <.radio_group field={@form[:plan]} label="Choose a plan">
    <:option value="free" label="Free" />
    <:option value="pro" label="Pro" />
  </.radio_group>

### With explicit errors

  <.radio_group field={@form[:plan]} label="Choose a plan" errors={["is required"]}>
    <:option value="free" label="Free" />
    <:option value="pro" label="Pro" />
  </.radio_group>

### Inside field composition

  <.field>
    <:label for={@form[:plan].id}>Plan</:label>
    <.radio_group field={@form[:plan]}>
      <:option value="free" label="Free" />
      <:option value="pro" label="Pro" />
    </.radio_group>
  </.field>

Screenshot

radio_group/1 screenshot

View live examples and full component docs.

select(assigns)

Renders a custom select with a button trigger and listbox content.

Use native_select/1 when you specifically want a plain HTML <select>.

## Examples

heex title="Custom select" align="full" <.select id="team-plan" name="plan" value="pro"> <:option value="free" label="Free" /> <:option value="pro" label="Pro" /> <:option value="enterprise" label="Enterprise" /> </.select>

heex title="Grouped labels" align="full" vrt <.select id="assignee" name="assignee" placeholder="Assign a teammate"> <:option value="levi" label="Levi" description="Platform" group="Engineering" /> <:option value="mira" label="Mira" description="Product Design" group="Design" /> </.select>

heex title="Disabled option" align="full" vrt <.select id="region" name="region"> <:option value="us" label="United States" /> <:option value="eu" label="Europe" /> <:option value="apac" label="APAC" disabled={true} /> </.select>

heex title="Clearable select" align="full" vrt <.select id="support-tier" name="tier" value="pro" clearable={true}> <:option value="free" label="Free" /> <:option value="pro" label="Pro" /> </.select>

### With FormField

  <.select field={@form[:role]}>
    <:option value="admin" label="Admin" />
    <:option value="member" label="Member" />
  </.select>

### With label

  <.select field={@form[:role]} label="Role">
    <:option value="admin" label="Admin" />
    <:option value="member" label="Member" />
  </.select>

### With explicit errors

  <.select field={@form[:role]} label="Role" errors={["is required"]}>
    <:option value="admin" label="Admin" />
  </.select>

### Inside field composition

  <.field>
    <:label for={@form[:role].id}>Role</:label>
    <.select field={@form[:role]}>
      <:option value="admin" label="Admin" />
    </.select>
    <:description>Choose your access level.</:description>
  </.field>

Screenshot

select/1 screenshot

View live examples and full component docs.

slider(assigns)

Renders a slider using native range input(s).

Use min, max, and step for scalar values. For range sliders, render two controls and sync values in LiveView.

## Examples

heex title="Basic slider" align="full" <.slider id="volume" name="volume" value={45} min={0} max={100} step={1} />

heex title="CPU limit slider" align="full" <.slider id="cpu_limit" name="cpu_limit" value={2} min={1} max={8} step={1} />

### With FormField

  <.slider field={@form[:volume]} />

### With label

  <.slider field={@form[:volume]} label="Volume" />

### With explicit errors

  <.slider field={@form[:volume]} label="Volume" errors={["is required"]} />

### Inside field composition

  <.field>
    <:label for={@form[:volume].id}>Volume</:label>
    <.slider field={@form[:volume]} />
    <:description>Drag to adjust volume level.</:description>
  </.field>

Screenshot

slider/1 screenshot

View live examples and full component docs.

switch(assigns)

Renders a switch control with optional label content.

## Examples

heex title="Basic switch" align="full" <.switch id="marketing" checked={true}>Email updates</.switch>

heex title="Disabled" align="full" <.switch id="notifications" disabled={true}>Push notifications</.switch>

### With FormField

  <.switch field={@form[:notifications]} />

### With label attr (inline)

  <.switch field={@form[:notifications]} label="Enable notifications" />

### With inner_block (takes precedence over label attr)

  <.switch field={@form[:notifications]}>
    Enable <strong>push</strong> notifications
  </.switch>

### With explicit errors

  <.switch field={@form[:notifications]} errors={["is required"]} />

### Inside field composition

  <.field>
    <:label for={@form[:notifications].id}>Notifications</:label>
    <.switch field={@form[:notifications]} />
  </.field>

Screenshot

switch/1 screenshot

View live examples and full component docs.

textarea(assigns)

Renders a textarea with shadcn classes.

## Examples

heex title="Basic textarea" align="full" <.textarea id="bio" name="bio" rows={4} />

heex title="With placeholder" align="full" <.textarea id="release_notes" name="release_notes" rows={8} placeholder="Summarize what changed in this release..." />

### With FormField

  <.textarea field={@form[:bio]} />

### With label

  <.textarea field={@form[:bio]} label="Bio" />

### With explicit errors

  <.textarea field={@form[:bio]} label="Bio" errors={["too short"]} />

### Inside field composition

  <.field>
    <:label for={@form[:bio].id}>Bio</:label>
    <.textarea field={@form[:bio]} />
    <:description>Tell us about yourself.</:description>
  </.field>

Screenshot

textarea/1 screenshot

View live examples and full component docs.