Fluxon.Components.Autocomplete (Fluxon v2.3.4)
A type-to-search input that filters and selects from a list of options, with client-side and server-side search support, keyboard navigation, and full Phoenix form integration.
The autocomplete component provides a combobox-style text input where users type to filter a dropdown of options. It is optimized for search-driven selection workflows of any dataset size, and offers customizable search modes, debounced server-side search, animated transitions, grouped options with nesting, custom option rendering, and rich empty-state handling.
Autocomplete vs Select
Both components let users choose from a list of options, but they target different interaction patterns. Use this table to decide which fits your use case.
| Feature | Select | Autocomplete |
|---|---|---|
| Primary interaction | Click to browse | Type to search |
| Best for | Small to medium lists | Any size dataset |
| Filtering | Client-side (+ optional server) | Client or server-side |
| Multiple selection | Yes | No |
| Clearable | Yes | Yes |
| Native mode | Yes | No |
| Custom empty state | No | Yes (:empty_state slot) |
| Open on focus | Always (click to open) | Optional (open_on_focus) |
Usage
Basic usage with a list of options:
<.autocomplete
name="country"
options={[{"United States", "us"}, {"Canada", "ca"}]}
placeholder="Search countries..."
/>The component follows the same options API as
Phoenix.HTML.Form.options_for_select/2,
supporting multiple formats:
# List of strings
<.autocomplete name="fruit" options={["Apple", "Banana", "Cherry"]} />
# List of tuples (label, value)
<.autocomplete name="country" options={[{"United States", "us"}, {"Canada", "ca"}]} />
# Keyword list
<.autocomplete name="lang" options={[English: "en", Spanish: "es", French: "fr"]} />
# Keyword list with key/value/disabled
<.autocomplete
name="role"
options={[
[key: "Admin", value: "admin"],
[key: "User", value: "user", disabled: true],
[key: "Guest", value: "guest"]
]}
/>
# Grouped options
<.autocomplete
name="city"
options={[
{"North America", [{"New York", "nyc"}, {"Toronto", "tor"}]},
{"Europe", [{"London", "lon"}, {"Paris", "par"}]}
]}
/>
# Multi-level nested groups (flattened with visual indentation)
<.autocomplete
name="city"
options={[
{"Europe", [
{"France", [{"Paris", 1}, {"Lyon", 2}]},
{"Italy", [{"Rome", 3}, {"Milan", 4}]}
]},
{"Asia", [
{"China", [{"Beijing", 5}, {"Shanghai", 6}]}
]}
]}
/>A full-featured example combining labels, description, help text, and placeholder:
<.autocomplete
name="deployment"
label="Deployment Platform"
sublabel="Optional"
description="This will determine your deployment configuration"
help_text="Choose from our supported platforms"
placeholder="Select platform..."
options={["Fly.io", "Railway", "Heroku", "Render", "AWS"]}
/>Size Variants
The autocomplete component offers five size variants to fit different interface densities:
<.autocomplete name="xs" size="xs" options={@options} placeholder="Extra small" />
<.autocomplete name="sm" size="sm" options={@options} placeholder="Small" />
<.autocomplete name="md" size="md" options={@options} placeholder="Medium (default)" />
<.autocomplete name="lg" size="lg" options={@options} placeholder="Large" />
<.autocomplete name="xl" size="xl" options={@options} placeholder="Extra large" />| Size | Height | Use Case |
|---|---|---|
"xs" | h-7 | Compact UIs, dense layouts, inline controls |
"sm" | h-8 | Secondary inputs, sidebar filters |
"md" | h-9 | Default -- recommended for most forms |
"lg" | h-10 | Prominent inputs, settings pages |
"xl" | h-11 | Hero sections, onboarding flows |
Each size variant adjusts height, padding, font size, icon dimensions, and affix sizing proportionally.
Labels and Descriptions
Provide context and guidance with comprehensive labeling:
# Basic label
<.autocomplete name="language" label="Language" options={@languages} />
# Label with sublabel
<.autocomplete name="language" label="Language" sublabel="Required" options={@languages} />
# Label with description
<.autocomplete
name="language"
label="Language"
description="Choose the primary language for your project"
options={@languages}
/>
# Complete example with help text
<.autocomplete
name="language"
label="Language"
sublabel="Optional"
description="Used for localization purposes"
help_text="You can change this later in settings"
placeholder="Search languages..."
options={@languages}
/>Form Integration
The autocomplete component integrates with Phoenix forms in two ways: using the field attribute for
full form integration or using the name attribute for standalone inputs.
Using with Phoenix Forms (Recommended)
Use the field attribute to bind the autocomplete to a form field:
<.form :let={f} for={@changeset} phx-change="validate" phx-submit="save">
<.autocomplete
field={f[:assigned_to_id]}
label="Assigned To"
options={@users}
/>
</.form>Using the field attribute provides:
- Automatic value handling from form data
- Error handling and validation messages
- Form submission with correct field names
- Integration with changesets
- ID generation for accessibility
Complete example with a changeset implementation:
defmodule MyApp.Task do
use Ecto.Schema
import Ecto.Changeset
schema "tasks" do
field :title, :string
belongs_to :assigned_to, MyApp.User
timestamps()
end
def changeset(task, attrs) do
task
|> cast(attrs, [:assigned_to_id, :title])
|> validate_required([:assigned_to_id])
end
end
# In your LiveView
def mount(_params, _session, socket) do
users = MyApp.Accounts.list_active_users() |> Enum.map(&{&1.name, &1.id})
changeset = Task.changeset(%Task{}, %{})
{:ok, assign(socket, users: users, form: to_form(changeset))}
end
def render(assigns) do
~H"""
<.form :let={f} for={@form} phx-change="validate">
<.autocomplete
field={f[:assigned_to_id]}
options={@users}
label="Assigned To"
placeholder="Search users..."
/>
</.form>
"""
end
def handle_event("validate", %{"task" => params}, socket) do
changeset =
%Task{}
|> Task.changeset(params)
|> Map.put(:action, :validate)
{:noreply, assign(socket, form: to_form(changeset))}
endUsing Standalone Autocomplete
For simpler cases or when not using Phoenix forms, use the name attribute:
<.autocomplete
name="search_user"
options={@users}
value={@user_id}
placeholder="Search users..."
/>When using standalone autocomplete:
- The
nameattribute determines the form field name - Values are managed through the
valueattribute - Errors are passed via the
errorsattribute
Input States
# Disabled state
<.autocomplete name="locked" disabled options={@options} value="locked_value" />
# Error state (standalone)
<.autocomplete name="country" options={@countries} errors={["is required"]} />
# Error state (form integration -- errors are handled automatically)
<.autocomplete field={f[:country]} options={@countries} />Search Behavior
The autocomplete component offers flexible search strategies to handle different use cases.
Client-Side Search
Client-side search provides immediate feedback as users type, filtering the provided options
directly in the browser. This is the default behavior (no on_search attribute).
Search Modes
Three search modes control how options are matched against the search query:
| Mode | Behavior | Use Case |
|---|---|---|
"contains" | Matches anywhere in the option label | General-purpose search (default) |
"starts-with" | Matches only at the beginning of the label | Alphabetical browsing, name lookup |
"exact" | Requires the query to match the label exactly | Precise matching, code lookup |
<.autocomplete
name="search"
search_mode="starts-with"
options={["JavaScript", "TypeScript", "CoffeeScript"]}
/>Search Implementation Details
The client-side search:
- Runs entirely in JavaScript for immediate feedback
- Is case-insensitive (normalizes to lowercase)
- Matches against the full option label text content
- Hides non-matching options without removing them from the DOM
- Automatically highlights the first matching option
Search Threshold
Control when filtering begins based on the number of characters typed:
# No threshold -- search starts on first character (default)
<.autocomplete name="search" search_threshold={0} options={@options} />
# Require at least 2 characters before searching
<.autocomplete name="search" search_threshold={2} options={@options} />Server-Side Search
For large datasets or dynamically computed results, enable server-side search by providing the
on_search attribute with a LiveView event name:
<.autocomplete
field={f[:user_id]}
on_search="search_users"
search_threshold={2}
debounce={500}
placeholder="Search users..."
options={@filtered_users}
/>When server-side search is enabled:
- The
on_searchevent is triggered after the debounce period - The event receives
%{"query" => query, "id" => component_id}as parameters - A loading spinner is displayed while the search is in progress
- The component reinitializes from the DOM once LiveView patches new options
def handle_event("search_users", %{"query" => query}, socket) do
users = MyApp.Accounts.search(query) |> Enum.map(&{&1.name, &1.id})
{:noreply, assign(socket, filtered_users: users)}
endImportant: Selected Options and Search Results
This is a fundamental constraint of server-side search that requires careful consideration.
The input label is derived from the currently available options in the options attribute.
If a user has selected an option and then the server responds with results that do not include
it, the label may become stale until the option reappears.
Recommended implementation: Always include the currently selected option in your server search results, even when it does not match the query. This prevents the label from disappearing.
Search Configuration
| Attribute | Default | Description |
|---|---|---|
search_threshold | 0 | Minimum characters required before triggering a search |
debounce | 200 | Milliseconds to wait after typing stops before searching |
on_search | nil | LiveView event name; when set, enables server-side search |
search_mode | "contains" | Client-side matching strategy ("contains", "starts-with", "exact") |
Clearable
The clearable attribute adds a clear button (X icon) inside the input that appears when a
value is selected. Clicking it resets both the visible input and the hidden form value:
<.autocomplete name="country" clearable options={@countries} placeholder="Select a country" />When the autocomplete is disabled, the clear button is hidden regardless of the clearable setting.
Disabled Options
Individual options can be disabled using the keyword list format with a disabled key:
<.autocomplete
name="role"
options={[
[key: "Admin", value: "admin"],
[key: "Editor", value: "editor", disabled: true],
[key: "Viewer", value: "viewer"]
]}
/>Disabled options:
- Cannot be selected via click or keyboard
- Are skipped during keyboard navigation
- Display with reduced opacity and a
not-allowedcursor - Have
aria-disabled="true"anddata-disabledattributes
Disabled options also work within grouped options:
<.autocomplete
name="user"
options={[
{"Active", [
[key: "Alice", value: "alice"],
[key: "Bob", value: "bob"]
]},
{"Inactive", [
[key: "Charlie", value: "charlie", disabled: true],
[key: "Diana", value: "diana", disabled: true]
]}
]}
/>Open on Focus
By default, the listbox opens when users start typing. Enable open_on_focus to show options
immediately when the input is focused, even before any characters are typed:
<.autocomplete
name="country"
open_on_focus
placeholder="Click to see all options..."
options={@countries}
/>open_on_focus and search_threshold interaction
The listbox only opens on focus if the current query length meets the search_threshold.
With the default threshold of 0, the listbox opens immediately. With search_threshold={2},
the user must type at least 2 characters before focus-triggered opening takes effect.
Inner Affixes
Add content inside the autocomplete's border using inner affix slots. Useful for icons, status indicators, and visual elements:
<.autocomplete name="user" options={@users} placeholder="Search users...">
<:inner_prefix>
<.icon name="hero-magnifying-glass" class="icon" />
</:inner_prefix>
</.autocomplete>
<.autocomplete name="email" options={@emails} placeholder="user@example.com">
<:inner_prefix>
<.icon name="hero-at-symbol" class="icon" />
</:inner_prefix>
<:inner_suffix>
<.icon name="hero-check-circle" class="icon text-green-500!" />
</:inner_suffix>
</.autocomplete>Outer Affixes
Place content outside the autocomplete's border using outer affix slots. Ideal for buttons, labels, and interactive elements:
<.autocomplete name="filtered" options={@options} placeholder="Search...">
<:outer_prefix class="px-3 text-foreground-soft">Filter:</:outer_prefix>
</.autocomplete>
<.autocomplete name="with_action" options={@options} placeholder="Search...">
<:outer_suffix>
<.button size="md">Search</.button>
</:outer_suffix>
</.autocomplete>Size Matching with Affixes
When using buttons or components within affix slots, match their
sizeattribute to the autocomplete'ssizefor proper visual alignment:<.autocomplete name="example" size="lg" options={@options}> <:outer_suffix> <.button size="lg">Action</.button> </:outer_suffix> </.autocomplete>
Custom Option Rendering
Customize how options appear in the dropdown through the :option slot. The component passes a
{label, value} tuple to the slot via :let:
<.autocomplete
name="user"
placeholder="Search users..."
options={[{"Alice Chen", "alice"}, {"Bob Smith", "bob"}]}
>
<:option :let={{label, value}}>
<div class={[
"flex items-center gap-3 p-2 rounded-lg",
"in-data-highlighted:bg-zinc-100",
"in-data-selected:font-medium"
]}>
<img src={"/avatars/#{value}.jpg"} class="size-8 rounded-full" />
<div>
<div class="text-sm font-medium">{label}</div>
<div class="text-xs text-zinc-500">@{value}</div>
</div>
</div>
</:option>
</.autocomplete>Input Label
The text input always displays the option's text label regardless of custom rendering.
For example, an option {"Alice Chen", "alice"} will show "Alice Chen" in the input when
selected, even if the dropdown renders it with an avatar and description.
Option States
Custom options can respond to these data attributes for state-aware styling:
| Attribute | Applied When | Use Case |
|---|---|---|
[data-highlighted] | Option is highlighted via keyboard or mouse hover | Hover/focus background |
[data-selected] | Option is currently selected | Checkmark, bold text, accent color |
[data-disabled] | Option is disabled | Reduced opacity, not-allowed cursor |
Use Tailwind's in-data-* variants to style ancestor-aware states:
<.autocomplete name="country" options={@countries}>
<:option :let={{label, value}}>
<div class={[
"flex items-center gap-2 px-3 py-2 cursor-default select-none",
"in-data-highlighted:bg-zinc-100",
"in-data-selected:font-medium",
"in-data-disabled:opacity-50"
]}>
<.icon name={"flag-#{String.downcase(value)}"} class="size-5" />
<span>{label}</span>
</div>
</:option>
</.autocomplete>Header and Footer Content
Add custom content at the top and bottom of the listbox using the :header and :footer slots.
Useful for filter buttons, action links, or informational text:
<.autocomplete field={f[:product]} options={@products}>
<:header class="p-2 border-b">
<div class="flex gap-2">
<.button size="xs" class="rounded-full" phx-click="filter" phx-value-type="all">All</.button>
<.button size="xs" class="rounded-full" phx-click="filter" phx-value-type="active">Active</.button>
</div>
</:header>
<:footer class="p-2 border-t">
<.button type="button" size="sm" class="w-full" as="link" navigate={~p"/products/new"}>
<.icon name="u-plus" class="size-4" /> Create new
</.button>
</:footer>
</.autocomplete>Both slots accept a class attribute for custom styling.
Empty State Customization
When no options match the search query, the component displays an empty state. There are two ways to customize it:
Default Text Message
Use the no_results_text attribute. The %{query} token is replaced with the search term
wrapped in <strong> tags:
<.autocomplete
name="search"
no_results_text="No countries matching '%{query}'"
options={@countries}
/>Custom Empty State Slot
For richer empty states with icons, buttons, or complex layouts, use the :empty_state slot.
When provided, it replaces the default text message:
<.autocomplete name="search" options={@items}>
<:empty_state>
<div class="p-4 text-center">
<p class="font-medium text-zinc-500">No results found</p>
<p class="text-sm text-zinc-400">Try a different search term</p>
</div>
</:empty_state>
</.autocomplete>Keyboard Navigation
The autocomplete component implements keyboard navigation following WAI-ARIA combobox patterns:
| Key | Context | Description |
|---|---|---|
Tab / Shift+Tab | Input | Moves focus to and from the input |
Down Arrow | Input (closed) | Opens the listbox and highlights the first option (or selected option) |
Up Arrow | Input (closed) | Opens the listbox and highlights the last option (or selected option) |
Down Arrow | Listbox open | Moves highlight to the next visible, non-disabled option |
Up Arrow | Listbox open | Moves highlight to the previous visible, non-disabled option |
Enter | Listbox open | Selects the highlighted option and closes the listbox |
Escape | Listbox open | Closes the listbox and returns focus to the input |
| Type characters | Input | Filters options based on typed text |
| Click outside | Any | Closes the listbox |
Highlight does not wrap
Unlike the select component, arrow key navigation in the autocomplete does not wrap around.
Pressing Down Arrow on the last option stays on the last option, and Up Arrow on the first
option stays on the first option.
Real-World Examples
Task Assignment with Avatars
<.autocomplete
field={f[:assigned_to_id]}
label="Assign To"
description="Select a team member to assign this task"
placeholder="Search by name..."
clearable
options={@users}
>
<:inner_prefix>
<.icon name="hero-user" class="icon" />
</:inner_prefix>
<:option :let={{label, value}}>
<div class="flex items-center gap-3 p-2">
<img src={"https://i.pravatar.cc/150?u=#{value}"} class="size-8 rounded-full" />
<div>
<div class="font-medium">{label}</div>
<div class="text-sm text-zinc-500">{user_email(value)}</div>
</div>
</div>
</:option>
</.autocomplete>Server-Side Search with Preselected Value
<.autocomplete
field={f[:customer_id]}
on_search="search_customers"
search_threshold={2}
debounce={400}
clearable
label="Customer"
placeholder="Type to search customers..."
no_results_text="No customers matching '%{query}'"
options={@customer_options}
/>def handle_event("search_customers", %{"query" => query, "id" => _id}, socket) do
selected_id = socket.assigns.form[:customer_id].value
results = MyApp.Customers.search(query)
# Always include the currently selected customer to prevent label disappearing
options =
if selected_id do
selected = MyApp.Customers.get!(selected_id)
[{selected.name, selected.id} | results] |> Enum.uniq_by(&elem(&1, 1))
else
results
end
{:noreply, assign(socket, customer_options: options)}
endE-commerce Product Search with Groups
<.autocomplete
name="product"
label="Product"
sublabel="Required"
help_text="Start typing to search through 1000+ products"
placeholder="Search products..."
clearable
search_threshold={3}
options={[
{"Electronics", [
{"iPhone 15 Pro", "iphone15pro"},
{"MacBook Air M2", "macbook-air-m2"}
]},
{"Clothing", [
{"Men's T-Shirt", "mens-tshirt"},
{"Winter Jacket", "winter-jacket"}
]}
]}
>
<:inner_prefix>
<.icon name="hero-magnifying-glass" class="icon" />
</:inner_prefix>
<:outer_suffix>
<.button size="md">Browse</.button>
</:outer_suffix>
</.autocomplete>Custom Empty State with Create Action
<.autocomplete
field={f[:tag_id]}
label="Tag"
placeholder="Search tags..."
options={@tags}
>
<:empty_state>
<div class="p-4 text-center">
<p class="font-medium text-zinc-500">No matching tags found</p>
<.button type="button" size="sm" class="mt-2" phx-click="create_tag">
<.icon name="u-plus" class="size-4" /> Create new tag
</.button>
</div>
</:empty_state>
</.autocomplete>Full-Featured Server Search with Custom Options
defmodule MyApp.UsersLive do
use MyAppWeb, :live_view
def mount(_params, _session, socket) do
{:ok, assign(socket, users: [], form: to_form(%{}, as: :assignment))}
end
def render(assigns) do
~H\"""
<.form :let={f} for={@form} phx-change="validate">
<.autocomplete
field={f[:user_id]}
options={Enum.map(@users, &{&1.name, &1.id})}
on_search="search_users"
search_threshold={2}
debounce={300}
clearable
label="Assign to"
placeholder="Search users..."
>
<:option :let={{label, value}}>
<div class="flex items-center gap-3 p-2 in-data-highlighted:bg-zinc-100">
<img src={user_avatar(value, @users)} class="size-8 rounded-full" />
<div>
<div class="font-medium text-sm">{label}</div>
<div class="text-xs text-zinc-500">{user_email(value, @users)}</div>
</div>
</div>
</:option>
<:empty_state>
<div class="p-4 text-center text-zinc-500">
<p>No matching users found</p>
<p class="text-sm">Try searching by name or email</p>
</div>
</:empty_state>
</.autocomplete>
</.form>
\"""
end
def handle_event("search_users", %{"query" => query}, socket) do
{:noreply, assign(socket, users: MyApp.Accounts.search(query))}
end
def handle_event("validate", _params, socket), do: {:noreply, socket}
end
Summary
Components
Renders an autocomplete component with type-to-search filtering and full keyboard navigation.
Components
Renders an autocomplete component with type-to-search filtering and full keyboard navigation.
This component provides a combobox-style text input for search-driven selection. It supports client-side and server-side filtering, custom option rendering, grouped options, and rich empty-state handling. Use it whenever users need to find and select a single value from a potentially large list. It includes built-in form integration, error handling, and accessibility features following WAI-ARIA combobox patterns.
Attributes
id(:any) - Sets the unique identifier for the autocomplete component. When not provided, defaults to thenameattribute value. Controls the IDs of the wrapper ({id}-autocomplete), text input ({id}), and listbox ({id}-listbox).Defaults to
nil.name(:any) - Specifies the form field name for the autocomplete. Required when not using thefieldattribute. This name is applied to the hidden input that submits the selected value.field(Phoenix.HTML.FormField) - Binds the autocomplete to a Phoenix form field. When provided, the component automatically extracts the name, id, value, and errors from the form data, and handles changeset integration. Takes precedence overname,value, anderrorsattributes.class(:any) - Specifies additional CSS classes to apply to the listbox container. Use for custom widths, max-heights, or other layout adjustments to the floating dropdown.Defaults to
nil.label(:string) - Sets the primary label text displayed above the input. Renders as an HTML<label>element linked to the text input for accessibility. Also used as thearia-labelon the combobox.Defaults to
nil.sublabel(:string) - Specifies secondary text displayed alongside the main label. Use for contextual hints like "Required", "Optional", or "Beta" without cluttering the primary label.Defaults to
nil.help_text(:string) - Sets help text displayed below the input field. Use for supplementary guidance or instructions that assist the user in making a selection.Defaults to
nil.description(:string) - Specifies a longer description displayed below the label but above the input element. Provides additional context about what the field is used for or how the selection will be applied.Defaults to
nil.placeholder(:string) - Sets the placeholder text displayed in the text input when no value is entered. Guides the user on what to type to begin searching.Defaults to
nil.autofocus(:boolean) - Controls whether the input element receives focus automatically when the page loads. Use sparingly -- only for the primary action on a page.Defaults to
false.disabled(:boolean) - Controls whether the autocomplete is disabled. Disabled autocompletes cannot be interacted with, appear visually muted, and are excluded from form submission. The clear button is hidden when disabled.Defaults to
false.size(:string) - Controls the visual size of the autocomplete component. Adjusts height, padding, font size, and icon dimensions proportionally."xs"-- Extra small (h-7), suitable for compact UIs and dense layouts"sm"-- Small (h-8), good for secondary inputs and sidebar filters"md"-- Medium (h-9), the default size recommended for most forms"lg"-- Large (h-10), suitable for prominent inputs and settings pages"xl"-- Extra large (h-11), ideal for hero sections and onboarding flows
Defaults to
"md".search_threshold(:integer) - Sets the minimum number of characters required before filtering options or triggering a server-side search. Useful for preventing unnecessary operations on very short queries. A value of0means filtering starts immediately on the first character.Defaults to
0.no_results_text(:string) - Specifies the message displayed when no options match the search query. Use%{query}as a placeholder token that will be replaced with the actual search term wrapped in<strong>tags. This message is only shown when no:empty_stateslot is provided.Defaults to
"No results found for \"%{query}\".".on_search(:string) - Specifies the LiveView event name to trigger for server-side searching. When set, the component dispatches afluxon:autocomplete:searchcustom event that the hook translates into apushEvent(on_search, %{"query" => query, "id" => id})call. The server should update theoptionsassign with the filtered results. Client-side filtering is bypassed when this is set.Defaults to
nil.debounce(:integer) - Controls the debounce time in milliseconds for server-side searches. Delays theon_searchevent to avoid excessive server calls while the user is typing. Only applies whenon_searchis set.Defaults to
200.search_mode(:string) - Controls the client-side search matching strategy. Only applies whenon_searchis not set."contains"-- Matches if the option label includes the query anywhere (default)"starts-with"-- Matches only if the option label starts with the query"exact"-- Matches only if the option label equals the query exactly
Defaults to
"contains".open_on_focus(:boolean) - Controls whether the listbox opens automatically when the input receives focus, even before any characters are typed. Whenfalse(default), the listbox only opens when the user starts typing. Thesearch_thresholdstill applies -- the listbox only opens on focus if the current query length meets the threshold.Defaults to
false.value(:any) - Sets the currently selected value. For standalone usage, pass a string or atom. When using thefieldattribute, this is automatically extracted from the form data. The component resolves the matching option label to display in the text input.errors(:list) - Specifies a list of error messages to display below the input. When present, the field wrapper receives adata-invalidattribute for error styling. Errors are automatically extracted when using thefieldattribute with form validation.Defaults to
[].options(:list) (required) - Specifies the list of options for the autocomplete. Supports multiple formats following thePhoenix.HTML.Form.options_for_select/2API:- Strings:
["Apple", "Banana"]-- label and value are the same - Tuples:
[{"Label", "value"}]-- explicit label/value pairs - Atoms:
[:admin, :user]-- converted to strings for both label and value - Integers:
[1, 2, 3]-- converted to strings - Keyword lists:
[key: "Admin", value: "admin"]-- with optionaldisabled: true - Keyword pairs:
[Admin: "admin", User: "user"] - Grouped:
[{"Group Label", [nested_options]}]-- supports unlimited nesting depth
- Strings:
animation(:string) - Sets the base CSS transition classes for the listbox open/close animation. Defaults to"transition duration-150 ease-in-out".animation_enter(:string) - Sets the CSS classes applied to the listbox when fully visible (enter animation end state). Defaults to"opacity-100 scale-100".animation_leave(:string) - Sets the CSS classes applied to the listbox when closing (leave animation end state). Defaults to"opacity-0 scale-95".clearable(:boolean) - Controls whether selections can be cleared. When enabled, displays a clear button (X icon) inside the input that appears when a value is selected. Clicking it resets both the visible text input and the hidden form value. The clear button is hidden when the component is disabled.Defaults to
false.Global attributes are accepted. Specifies additional HTML attributes passed through to the hidden
<input>element. Supports theformattribute to associate with a form by ID. Supports all globals plus:["form"].
Slots
inner_prefix- Renders content inside the input field's border, before the text entry area. Ideal for icons or short textual prefixes that should appear within the input area. Icon sizing is automatically adjusted based on the autocomplete'ssizeattribute.Accepts attributes:class(:any) - Specifies CSS classes for the inner prefix container.
outer_prefix- Renders content outside and before the input field's border. Suitable for standalone labels, dropdown menus, or buttons that visually attach to the autocomplete's left edge. When containing aFluxon.Components.Button, the border is automatically hidden to avoid double borders.Accepts attributes:class(:any) - Specifies CSS classes for the outer prefix container.
inner_suffix- Renders content inside the input field's border, after the text entry area. Suitable for status icons, validation indicators, or informational elements. Appears after the clear button whenclearableis enabled.Accepts attributes:class(:any) - Specifies CSS classes for the inner suffix container.
outer_suffix- Renders content outside and after the input field's border. Ideal for action buttons, submit controls, or links that visually attach to the autocomplete's right edge. When containing aFluxon.Components.Button, the border is automatically hidden to avoid double borders.Accepts attributes:class(:any) - Specifies CSS classes for the outer suffix container.
option- Defines custom rendering for each option in the listbox. Receives a{label, value}tuple via:let. When not provided, options render with the default layout (label text plus a checkmark icon for selected state). Custom options can usein-data-highlighted,in-data-selected, andin-data-disabledTailwind variants for state-aware styling.Accepts attributes:class(:any) - Specifies additional CSS classes for the option wrapper.
empty_state- Defines custom rendering for the empty state shown when no options match the search query. When provided, replaces the defaultno_results_textmessage entirely. Useful for rich empty states with icons, illustrations, or action buttons (e.g., "Create new item").Accepts attributes:class(:any) - Specifies additional CSS classes for the empty state container.
header- Renders custom content at the top of the listbox, above the options list. Useful for filter buttons, category tabs, or informational banners.Accepts attributes:class(:any) - Specifies additional CSS classes for the header container.
footer- Renders custom content at the bottom of the listbox, below the options list. Ideal for "Create new" links, summary text, or action buttons.Accepts attributes:class(:any) - Specifies additional CSS classes for the footer container.