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.

Select your primary role

Choose your role to customize your experience

Affects available features and reports
<.autocomplete
  name="provider"
  label="Watch Provider"
  options={[
    {"Netflix", "netflix"},
    {"Amazon Prime Video", "amazon"},
    {"HBO Max", "hbo"}
  ]}
/>

Search Behavior

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"},
    # ...
  ]}
/>

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

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.

Search company departments

<.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"}
      # ...
    ]
  }
/>