Enhanced tree view components for PhiaUI.
10 components extending the base tree/1 and tree_item/1 with icons,
tri-state checkboxes, file-system display, search filtering, lazy loading,
and virtual rendering for large datasets.
Component Overview
| Component | Tier | Key Feature |
|---|---|---|
icon_tree | :widget | Tree root with icon support |
icon_tree_item | :widget | Leaf/branch + Lucide icon + badge |
checkbox_tree | :interactive | Tri-state checkbox tree root |
checkbox_tree_item | :interactive | Checkbox item with indeterminate state |
searchable_tree | :widget | Tree with search input above |
file_tree | :widget | File-system display tree root |
file_tree_item | :widget | File/folder item with extension icon |
lazy_tree | :interactive | Tree root with lazy-load hook |
lazy_tree_item | :interactive | Item with loading/loaded states |
virtual_tree | :widget | Hook-owned virtual rendering tree |
Hooks required
PhiaCheckboxTree— checkbox_tree, checkbox_tree_item (sets.indeterminate)PhiaLazyTree— lazy_tree, lazy_tree_item (fires expand events)PhiaVirtualTree— virtual_tree (owns DOM rendering)
Summary
Functions
Checkbox tree root. Requires PhiaCheckboxTree hook to set the
.indeterminate JS property on items with data-indeterminate="true".
Checkbox tree item with tri-state support.
File-system display tree root.
File or folder tree item with extension-based icon.
Tree root with icon-item support.
Tree item with optional icon, badge, href, and expandable branch support.
Tree root with lazy-load support via the PhiaLazyTree hook.
Lazy tree item. Fires an expand event to load children on first open.
Tree with a search input above. Filtering logic lives in the LiveView.
Virtual-rendered tree for large datasets (1000+ nodes).
Functions
Checkbox tree root. Requires PhiaCheckboxTree hook to set the
.indeterminate JS property on items with data-indeterminate="true".
Example
<.checkbox_tree id="permission-tree">
<.checkbox_tree_item
id="perm-read"
label="Read"
checked={:read in @permissions}
on_check="toggle_permission"
value="read"
/>
</.checkbox_tree>Attributes
id(:string) (required) - Unique ID (required for phx-hook).class(:string) - Defaults tonil.- Global attributes are accepted.
Slots
inner_block(required)
Checkbox tree item with tri-state support.
The .indeterminate JS property is set by the PhiaCheckboxTree hook on
mounted() and updated() when data-indeterminate="true".
Example
<.checkbox_tree_item
id="item-files"
label="Files"
checked={:files in @checked}
indeterminate={has_partial_check?(@checked, @file_ids)}
on_check="toggle_perm"
value="files"
>
<.checkbox_tree_item id="item-read" label="Read" checked={:read in @checked} />
</.checkbox_tree_item>Attributes
id(:string) (required) - Unique ID for the checkbox element.label(:string) (required)checked(:boolean) - Defaults tofalse.indeterminate(:boolean) - Server-computed partial-check state. Defaults tofalse.on_check(:string) - phx-click event name. Defaults to"check_tree_item".value(:string) - phx-value-value sent on check. Defaults tonil.depth(:integer) - Indentation depth (multiplied by ml-4). Defaults to0.class(:string) - Defaults tonil.
Slots
inner_block
File-system display tree root.
Example
<.file_tree id="project-files">
<.file_tree_item label="src" type={:folder} expanded={true}>
<.file_tree_item label="app.ex" type={:file} />
</.file_tree_item>
</.file_tree>Attributes
id(:string) (required)class(:string) - Defaults tonil.- Global attributes are accepted.
Slots
inner_block(required)
File or folder tree item with extension-based icon.
Folders show a folder icon and use <details>/<summary>.
Files show a file-type icon based on their extension.
Example
<.file_tree_item label="mix.exs" type={:file} on_click="open" value="mix.exs" />
<.file_tree_item label="lib" type={:folder} expanded={true}>
<.file_tree_item label="app.ex" type={:file} />
</.file_tree_item>Attributes
label(:string) (required) - File or folder name.type(:atom) - Item type. Defaults to:file. Must be one of:file, or:folder.expanded(:boolean) - Defaults tofalse.selected(:boolean) - Defaults tofalse.size(:string) - Optional file size string (e.g. '4.2 KB'). Defaults tonil.modified(:string) - Optional last-modified string. Defaults tonil.on_click(:string) - Defaults tonil.value(:string) - Defaults tonil.class(:string) - Defaults tonil.
Slots
inner_block
Tree root with icon-item support.
Example
<.icon_tree id="nav-tree">
<.icon_tree_item label="Components" icon="layers" expandable={true}>
<.icon_tree_item label="Button" icon="square" />
</.icon_tree_item>
</.icon_tree>Attributes
id(:string) (required)class(:string) - Defaults tonil.- Global attributes are accepted.
Slots
inner_block(required)
Tree item with optional icon, badge, href, and expandable branch support.
Uses <details>/<summary> for branches (zero-JS expand/collapse).
Disabled items are non-interactive and visually dimmed.
Example
<.icon_tree_item label="Settings" icon="settings" on_click="nav" value="settings" />
<.icon_tree_item label="src" icon="folder" expandable={true} expanded={true}>
<.icon_tree_item label="app.ex" icon="file-code" />
</.icon_tree_item>Attributes
label(:string) (required)icon(:string) - Lucide icon name shown before the label. Defaults tonil.badge(:string) - Optional badge text. Defaults tonil.badge_variant(:string) - Badge color variant string. Defaults to"secondary".href(:string) - When set, renders label as<a>link. Defaults tonil.expandable(:boolean) - Defaults tofalse.expanded(:boolean) - Defaults tofalse.selected(:boolean) - Defaults tofalse.disabled(:boolean) - Defaults tofalse.on_click(:string) - Defaults tonil.value(:string) - Defaults tonil.class(:string) - Defaults tonil.
Slots
inner_block
Tree root with lazy-load support via the PhiaLazyTree hook.
When a lazy_tree_item is expanded by the user, the hook fires
push_event(@on_expand, %{"id" => item_id}) to the LiveView, which
can load children and update the item's loaded assign.
Example
<.lazy_tree id="repo-tree" on_expand="load_children">
<.lazy_tree_item id="src" label="src" expandable={true} loaded={@src_loaded}>
<.lazy_tree_item :for={f <- @src_files} id={f.id} label={f.name} />
</.lazy_tree_item>
</.lazy_tree>Attributes
id(:string) (required) - Unique ID (required for phx-hook).on_expand(:string) - push_event name when a node expands. Defaults to"tree_expand".class(:string) - Defaults tonil.- Global attributes are accepted.
Slots
inner_block(required)
Lazy tree item. Fires an expand event to load children on first open.
When loading is true, shows a spinner instead of children.
When loaded is true, children are rendered normally.
Example
<.lazy_tree_item id="src-dir" label="src" expandable={true} loaded={@src_loaded} loading={@src_loading}>
<.lazy_tree_item :for={f <- @src_files} id={f.id} label={f.name} />
</.lazy_tree_item>Attributes
id(:string) (required) - Node ID sent to the hook on expand.label(:string) (required)expandable(:boolean) - Defaults tofalse.expanded(:boolean) - Defaults tofalse.loading(:boolean) - Shows a spinner while children load. Defaults tofalse.loaded(:boolean) - When true, children are present and loading is done. Defaults tofalse.class(:string) - Defaults tonil.
Slots
inner_block
Tree with a search input above. Filtering logic lives in the LiveView.
The search input fires on_search with phx-debounce="200". Your
LiveView filters the tree data and re-renders only matching items.
Example
<.searchable_tree id="file-search" on_search="filter_files" search_value={@search}>
<.icon_tree_item :for={file <- @filtered_files} label={file.name} icon="file" />
</.searchable_tree>Attributes
id(:string) (required)search_placeholder(:string) - Defaults to"Search...".on_search(:string) - phx-change event for search input. Defaults to"search_tree".search_value(:string) - Current search value for controlled input. Defaults to"".class(:string) - Defaults tonil.
Slots
inner_block(required)
Virtual-rendered tree for large datasets (1000+ nodes).
The PhiaVirtualTree hook owns the DOM — only visible rows are rendered.
Initial node data is passed as data-nodes JSON; subsequent updates are
sent via push_event("update-nodes", %{nodes: [...]}).
Use phx-update="ignore" (set automatically) to prevent LiveView from
reconciling the hook-managed DOM.
Example
<.virtual_tree
id="large-tree"
nodes={@flat_nodes}
row_height={28}
height={500}
/>LiveView update
{:noreply, push_event(socket, "update-nodes", %{nodes: flat_nodes})}Attributes
id(:string) (required) - Unique ID (required for phx-hook).nodes(:list) (required) - Flat list of node maps with :id, :label, :depth, :expandable, :expanded.row_height(:integer) - Height of each rendered row in pixels. Defaults to32.height(:integer) - Container height in pixels (viewport). Defaults to400.class(:string) - Defaults tonil.