Components

Forms

PhoenixPaper.Input, Select, NumberField, Checkbox, Switch, ThemeToggle, RadioGroup, Slider, Rating, Autocomplete, TransferList. Every one of these also accepts a field from to_form/2, the same way a generated core_components.ex input does.

Input

Modeled on MUI's TextField. Three variants (outlined, filled, standard), pure-CSS floating label, no JavaScript.

Variants

States

We'll never share your email.

is not a valid email

Colors

Size and adornments

$
USD

Multiline

Options

label / value / name / id
standard text field attrs
type
any input type, e.g. text | email | password (default: text)
variant
outlined | filled | standard (default: outlined)
color
primary | secondary | tertiary | error (default: primary), focus/label accent
size
medium | small (default: medium)
shape
corner radius token (default: :sm), ignored for variant="standard"
multiline / rows
renders a textarea instead of an input
start_adornment / end_adornment
slots for prefix/suffix content, e.g. an icon or unit
field
a Phoenix.HTML.FormField from to_form/2: sets name/id/value for you
errors
list of error strings: switches to the error color, hides helper_text
helper_text
shown below the field when there are no errors
disabled
boolean (default: false)
paperize
boolean (default: true)

Select

A native select element styled to match Input's outlined/filled variants.

Variants

Options

options
list of {label, value} tuples, or plain values
prompt
an empty/placeholder option's label
variant
outlined | filled (default: outlined)
field / errors / helper_text
same as Input
disabled
boolean (default: false)

Number Field

A numeric input with increment/decrement stepper buttons: plain onclick JS calling stepUp()/stepDown(), no JS hook.

Variants

Options

min / max / step
passed straight to the underlying input type="number"
variant / shape / field / errors / helper_text
same as Input
disabled
boolean (default: false)

Checkbox

Includes the hidden-input trick so an unchecked box still submits false.

States

Options

checked
boolean (default: nil, meaning unchecked)
field
a Phoenix.HTML.FormField: sets name/id/checked for you
label
text next to the box
disabled
boolean (default: false)
ripple
boolean, the ripple effect on click/tap (default: false)
paperize
false renders a bare native checkbox, no hidden input

Switch

An on/off toggle, structured like Checkbox but rendered as a sliding track/thumb.

States

Options

checked / field / label / disabled / ripple / paperize
same shape as Checkbox

Theme Toggle

A light/dark mode toggle built on top of Switch's own markup (sun/moon icons live inside the sliding thumb). Wired with a small vanilla onclick that flips data-theme on the target element, computing the effective theme itself rather than trusting the checkbox, no server round-trip needed.

Try it (flips this whole page's theme)

default label
icon-only (label: nil)

This site's own toggle in the top-right corner is this exact component. It doesn't persist across a full page reload by default (that's what on_toggle is for, e.g. JS.push to save the choice server-side); within a session, LiveView's own navigate-based routing keeps it in place as you move between pages, and it already falls back to the OS/browser's color-scheme preference with zero clicks, via CSS.

Options

label
text next to the switch (default: "Dark mode"), nil for icon-only
default_checked
boolean, initial visual state, uncontrolled (default: false)
target
CSS selector for the element to toggle data-theme on (default: "html")
on_toggle
extra JS commands run before the built-in flip, e.g. to persist server-side
ripple / paperize
same as Switch

Radio Group

A labeled set of mutually exclusive radio buttons sharing one name.

Options

Size

Options

options
list of {label, value} tuples, or plain values
value
the currently selected value
label
the group's legend
ripple
boolean, the ripple effect on click/tap (default: false)
field / disabled / paperize
same as other form controls

Slider

A native range input, fully re-skinned via ::-webkit-slider-thumb / ::-moz-range-progress rather than CSS accent-color alone, so the unfilled portion of the track can be controlled too.

Colors

primary 60
secondary 60
tertiary 60
error 60

Track modes

track: none 60
track: inverted 60

Marks

Discrete (marks) 40
Custom labeled marks 30
0°C 30°C 60°C 100°C

Range, size, disabled

Range slider 20 – 80
Small 60
Disabled 30

Vertical

Options

min / max / step
default 0 / 100 / 1
value
a number, or a {low, high} tuple for a range slider (two thumbs)
color
primary | secondary | tertiary | error (default: primary)
size
medium | small (default: medium)
orientation
horizontal | vertical (default: horizontal)
track
normal | none | inverted (default: normal), ignored for range sliders
marks
true (tick every step), a list of values, or a list of {value, label} tuples
label
shown above the slider with the current value
field / disabled / paperize
same as other form controls

Rating

A row of radio inputs with a pure-CSS hover/checked fill effect: hovering star 3 highlights stars 1-3, no JS.

Interactive

Read-only

Options

value
integer, the current/selected rating (default: 0)
max
number of stars (default: 5)
readonly
boolean, renders fixed filled/unfilled spans instead of inputs (default: false)
field / disabled / paperize
same as other form controls

Autocomplete

LiveComponent

A text field with a filtered dropdown, filtered entirely server-side over phx-change/phx-debounce. Unlike everything above, this needs interactive state, so it's a Phoenix.LiveComponent, fully live on this page, since it's a real LiveView. Type to filter.

Try it

Options

options
list of {label, value} tuples, or plain values
value / name / label / placeholder
same intent as Input
shape / paperize
same as other form controls

Transfer List

LiveComponent

Two list boxes with buttons to move checked items between them, state managed entirely inside the component. Also a Phoenix.LiveComponent: try checking a permission and moving it across.

Try it

Available (5)
Granted (0)

Options

items
the starting list: everything begins on the left
left_label / right_label
column headers (default: "Available" / "Selected")
z7ealth 2026