Autocomplete
A searchable input component with real-time filtering and custom rendering.
<.autocomplete
name="provider"
label="Watch Provider"
options={[
{"Netflix", "netflix"},
{"Amazon Prime Video", "amazon"},
{"HBO Max", "hbo"}
]}
/>
Basic Configuration
Configure the autocomplete with labels, sublabels, descriptions, and help text to provide context for the search field.
Choose your role to customize your experience
<.autocomplete
name="provider"
label="Watch Provider"
options={[
{"Netflix", "netflix"},
{"Amazon Prime Video", "amazon"},
{"HBO Max", "hbo"}
]}
/>
Search Behavior
Client-side Search
Filter options directly in the browser using different matching strategies: contains, starts-with, or exact match.
<.autocomplete
name="genre"
label="Genre"
search_mode="starts-with"
options={[
{"Action", "action"},
{"Comedy", "comedy"},
{"Drama", "drama"},
# ...
]}
/>
Server-side Search
Handle option filtering through LiveView events for large datasets or complex search logic.
<.autocomplete
name="movie"
label="Movies"
class="max-w-[400px] max-h-[400px]"
options={Enum.map(@movies, &{&1.title, &1.id})}
on_search="search_movies"
/>
<!--
# LiveView implementation example:
def mount(socket) do
{:ok, movies} = MyApp.Movies.search("batman")
{:ok, socket |> assign(movies: movies)}
end
def handle_event("search_movies", %{"query" => query}, socket) do
{:ok, movies} = MyApp.Movies.search(query)
{:noreply, socket |> assign(movies: movies)}
end
-->
Option Presentation
Grouped Options
Organize options into logical groups using nested arrays of options.
<.autocomplete
name="company"
label="Production Company"
options={[
{"United States",
[
{"Walt Disney Pictures", "disney"},
{"Warner Bros. Pictures", "warner"},
...
]},
{"United Kingdom",
[
{"Working Title Films", "working_title"},
{"BBC Films", "bbc"},
...
]}
]}
/>
Slot Customization
Header and Footer
Add custom content like action buttons or filters at the top and bottom of the options list using header and footer slots.
<.autocomplete
name="studio"
label="Film Studio"
options={[
{"Warner Bros.", "warner"},
{"Universal Pictures", "universal"},
# ...
]}
>
<:header class="p-2 text-sm text-gray-500 pb-0">
<.button size="xs" class="rounded-full">Major Studios</.button>
<.button size="xs" class="rounded-full">Independent</.button>
<.button size="xs" class="rounded-full">Animation</.button>
</:header>
<:footer class="p-2 border-t text-center">
<.button type="button" size="sm" class="w-full">
<.icon name="hero-plus" class="icon" /> Add Studio
</.button>
</:footer>
</.autocomplete>
Custom Options
Render rich content in options using custom HTML and state-based styling.
<.autocomplete field={f[:user_id]} options={Enum.map(@users, &{&1.full_name, &1.id})} on_search="search_users">
<:option :let={{label, value}}>
<div class="flex items-center gap-3 p-2 rounded-lg [[data-highlighted]_&]:bg-zinc-100 [[data-selected]_&]:bg-blue-100">
<img src={"https://i.pravatar.cc/150?u=#{value}"} class="size-8 rounded-full" />
<div>
<div class="font-medium text-sm">{label}</div>
<div class="text-xs text-zinc-500">{user_by_id(value, @users).email}</div>
</div>
</div>
</:option>
</.autocomplete>
Empty State
Customize the content displayed when no options match the search query.
Create new department
<.modal id="new-department">
<!-- form submits to save_department -->
</.modal>
<.form for={@form} phx-submit="save">
<.autocomplete
field={@form[:department]}
label="Department"
placeholder="Select a department"
description="Search company departments"
options={@departments}
>
<:empty_state>
<.button class="w-full" variant="ghost" size="sm" type="button" phx-click={Fluxon.open_dialog("new-department")}>
<.icon name="hero-plus" class="size-4" /> Create new department
</.button>
</:empty_state>
</.autocomplete>
</.form>
<!--
def handle_event("save_department", %{"name" => name}, socket) do
departments = [...] # Save and update the list to include the new department.
form = to_form(...) # Update the form to reflect the newly created department.
{:noreply,
socket
|> assign(departments: departments, form: form)
|> Fluxon.close_dialog("new-department")}
end
-->
Interaction
Clearable
Enable option deselection with a clear button and keyboard support.
<.autocomplete
name="director"
label="Director"
value="nolan"
clearable
options={[
{"Christopher Nolan", "nolan"},
{"Steven Spielberg", "spielberg"},
{"Quentin Tarantino", "tarantino"},
# ...
]}
/>
Open on Focus
Show the options list when the input receives focus, before any search query.
<.autocomplete
name="cinematographer"
label="Cinematographer"
open_on_focus
options={
[
{"Roger Deakins", "deakins"},
{"Emmanuel Lubezki", "lubezki"},
{"Robert Richardson", "richardson"}
# ...
]
}
/>