PhiaUi.Components.DataTable (phia_ui v0.1.17)

Copy Markdown View Source

All-in-one declarative table — define columns once, render everything.

Unlike the composable Table primitive (where you manually write thead/tbody rows), DataTable generates the full table structure from a :rows list and named :column slots. This makes common tables fast to build with minimal boilerplate.

For advanced use cases (streams, row selection, pagination, column toggle) use DataGrid instead.

Sub-components

FunctionUsed asPurpose
data_table/1ComponentOuter table + header + body
:column slot<:column>Column definition with inner_block

Example

<.data_table
  rows={@users}
  sort_key={@sort_key}
  sort_dir={@sort_dir}
  on_sort="sort"
  striped
>
  <:column key="name" label="Name" sortable :let={user}>
    <span class="font-medium">{user.name}</span>
  </:column>
  <:column key="email" label="Email" sortable :let={user}>
    {user.email}
  </:column>
  <:column key="role" label="Role" align={:center} :let={user}>
    <.badge variant={:secondary}>{user.role}</.badge>
  </:column>
  <:action :let={user}>
    <.button variant={:ghost} size={:sm} phx-click="edit" phx-value-id={user.id}>
      Edit
    </.button>
  </:action>
</.data_table>

Sort events

Sortable column headers fire the on_sort event with phx-value-key and phx-value-dir (the next direction: "asc", "desc", or "none"):

def handle_event("sort", %{"key" => key, "dir" => dir}, socket) do
  {:noreply, assign(socket, sort_key: key, sort_dir: String.to_existing_atom(dir))}
end

Summary

Functions

Renders a complete table from rows and :column slot definitions.

Functions

data_table(assigns)

Renders a complete table from rows and :column slot definitions.

Column headers are generated from :column slot attrs (label, sortable, align). Each body row is built by iterating rows and calling render_slot(col, row) for each column — the slot's :let binding receives the row map/struct.

Example

<.data_table rows={@products} empty_message="No products yet">
  <:column key="name" label="Product" :let={p}>{p.name}</:column>
  <:column key="price" label="Price" align={:right} :let={p}>
    ${p.price}
  </:column>
</.data_table>

Attributes

  • rows (:list) (required) - List of maps or structs to render — one row per element.

  • row_id (:any) - Optional function fn row -> id end to extract a stable row identifier. Not used directly in the render but available for LiveView stream integration.

    Defaults to nil.

  • sort_key (:string) - Key of the currently sorted column. Matches the key attr on :column slots. Defaults to nil.

  • sort_dir (:atom) - Current sort direction. :none shows the unsorted double-chevron icon. Defaults to :none. Must be one of :none, :asc, or :desc.

  • on_sort (:string) - phx-click event fired when a sortable column header is clicked. Receives phx-value-key (column key) and phx-value-dir (next direction string).

    Defaults to "sort".

  • striped (:boolean) - When true, alternating rows receive a subtle bg-muted/30 background. Defaults to false.

  • empty_message (:string) - Text displayed when rows is an empty list. Defaults to "No data".

  • class (:string) - Additional CSS classes for the outer wrapper div. Defaults to nil.

Slots

  • column - Accepts attributes:

    • key (:string) (required)
    • label (:string) (required)
    • sortable (:boolean)
    • align (:atom) - Must be one of :left, :right, or :center.
    • width (:string)
  • action - Optional per-row action cell appended as the last column. The slot receives each row via :let:

    <:action :let={user}>
      <.button phx-click="delete" phx-value-id={user.id}>Delete</.button>
    </:action>