I've been exploring ways to create reusable UI components with Elixir and Phoenix LiveView. Here's my latest creation: a flexible, interactive tag input component that combines server-side rendering with client-side interactivity using LiveView Hooks.
What We're Building
The tag input component allows users to:
- Add items by typing and pressing Enter or Tab
- Remove items with a button or backspace (when enabled)
- Enforce a maximum item limit
- Detect and animate duplicate entries
- Provide autocomplete suggestions (optional)
- Submit items as part of a form
Here's a quick demo of what it looks like:
<List.root id="tags" name="tags" max_items="5" remove_on_backspace?>
<List.item :for={item <- @tags} name="tags" value={item} animate_duplicate?>
<span>{item}</span>
<List.item_remove>X</List.item_remove>
</List.item>
<List.input id="tags" placeholder="Add a tag" />
<List.options :if={@options != []} id="tags" limit_input_to_options?>
<List.option :for={{value, label} <- @options} value={value} label={label}>
{label}
</List.option>
</List.options>
</List.root>
The Architecture
This component is split into three main parts:
- Elixir Components: Phoenix components for server-side rendering and form integration
- JavaScript Hooks: LiveView Hooks for client-side interactivity
- CSS: TailwindCSS classes for styling (customizable)
Server-Side: Elixir Components
The Elixir side uses two main modules:
-
ListInputUI.Headless.List
: Defines the core list structure with components likeroot
,item
,input
, andoptions
these component don't assume anything about your styles -
ListInputUI.Field
: Provides a form-aware wrapper with labels, error handling and styling
Client-Side: LiveView Hooks
Two JavaScript hooks power the interactivity:
-
ListInputHook
:- Manages the core list functionality
- Adds/removes items
- Enforces max items and duplicate detection
- Triggers change events for LiveView updates
-
ListInputOptionsHook
:- Manages the autocomplete dropdown
- Filters options based on input
- Handles keyboard navigation (Arrow keys, Enter)
- Highlights matching text using CSS Highlights API
Integration with LiveView
The hooks integrate seamlessly with LiveView by:
- Attaching to DOM elements via
phx-hook
- Dispatching
change
events that trigger LiveView form updates - Using hidden inputs to submit data with forms
Next Steps
I plan to enhance this component by:
- Improving accessibility with ARIA attributes
- Show examples for searchable options in dropdown from the server
- Create a video of how i've done this.
You can find the full source code on @Gumroad, don't worry it's free if you choose so.
Top comments (0)