Inline-editable table row components for in-place data editing.
All state (which rows are in edit mode, saving state) lives in the LiveView.
These components are purely presentational — they switch between a read view
and an edit view based on @editing assigns.
Sub-components
| Function | HTML element | Purpose |
|---|---|---|
editable_cell/1 | <td> | Single cell toggling view ↔ edit mode |
editable_row/1 | <tr> | Row wrapper that applies edit-mode styling |
editable_row_actions/1 | <td> | Save + Cancel action cell for editing rows |
Example
defmodule MyAppWeb.ProductsLive do
use MyAppWeb, :live_view
def handle_event("start_edit", %{"id" => id}, socket) do
{:noreply, assign(socket, editing_id: id)}
end
def handle_event("save_row", %{"id" => id}, socket) do
# persist changes, then:
{:noreply, assign(socket, editing_id: nil)}
end
def handle_event("cancel_edit", _params, socket) do
{:noreply, assign(socket, editing_id: nil)}
end
def render(assigns) do
~H"""
<.table>
<.table_body>
<%= for product <- @products do %>
<% editing = to_string(product.id) == @editing_id %>
<.editable_row editing={editing}>
<.editable_cell editing={editing}>
<:view>{product.name}</:view>
<:edit>
<input type="text" name="name" value={product.name}
class="h-8 w-full rounded border border-input px-2 text-sm" />
</:edit>
</.editable_cell>
<.editable_cell editing={editing}>
<:view>{product.price}</:view>
<:edit>
<input type="number" name="price" value={product.price}
class="h-8 w-24 rounded border border-input px-2 text-sm text-right" />
</:edit>
</.editable_cell>
<%= if editing do %>
<.editable_row_actions
row_id={to_string(product.id)}
on_save="save_row"
on_cancel="cancel_edit"
/>
<% else %>
<.table_cell>
<.button variant={:ghost} size={:sm}
phx-click="start_edit"
phx-value-id={to_string(product.id)}>Edit</.button>
</.table_cell>
<% end %>
</.editable_row>
<% end %>
</.table_body>
</.table>
"""
end
end
Summary
Functions
Renders a <td> that switches between a read view and an edit view.
Renders a <tr> wrapper that applies edit-mode visual styling.
Renders a <td> containing Save and Cancel buttons for an editing row.
Functions
Renders a <td> that switches between a read view and an edit view.
Only one slot is rendered at a time — determined by @editing. This keeps
the server-rendered HTML minimal while making the transition seamless from
the user's perspective.
Example
<.editable_cell editing={@editing_id == product.id}>
<:view>{product.sku}</:view>
<:edit>
<input type="text" name="sku" value={product.sku}
class="h-8 w-full rounded border border-input px-2 text-sm" />
</:edit>
</.editable_cell>Attributes
editing(:boolean) - Whentrue, the:editslot is displayed and:viewis hidden. Whenfalse, the:viewslot is displayed and:editis hidden.Defaults to
false.class(:string) - Additional CSS classes for the<td>. Defaults tonil.Global attributes are accepted. HTML attributes forwarded to the
<td>element.
Slots
view(required) - Content shown when the row is NOT in edit mode (read view).edit(required) - Input or select shown when the row IS in edit mode (edit view).
Renders a <tr> wrapper that applies edit-mode visual styling.
When editing={true}:
- Adds a subtle
bg-muted/20background - Adds
ring-1 ring-inset ring-primary/30to signal an active edit session - Sets
data-editing="true"for CSS or JavaScript targeting
Example
<.editable_row editing={@editing_id == user.id}>
<.editable_cell editing={@editing_id == user.id}>
<:view>{user.name}</:view>
<:edit><input ... /></:edit>
</.editable_cell>
<.editable_row_actions row_id={to_string(user.id)} />
</.editable_row>Attributes
editing(:boolean) - Whentrue, applies an edit-mode background tint and a primary ring outline to the row. Set tofalsefor the default read view.Defaults to
false.class(:string) - Additional CSS classes for the<tr>. Defaults tonil.Global attributes are accepted. HTML attributes forwarded to the
<tr>element (e.g.id).
Slots
inner_block(required) -editable_cell/1andeditable_row_actions/1children.
Renders a <td> containing Save and Cancel buttons for an editing row.
The Save button is styled as a primary action. When saving={true} it
becomes dimmed to signal an in-flight request. The Cancel button is a ghost
action that discards changes.
Example
<.editable_row_actions
row_id={to_string(user.id)}
saving={@saving_id == user.id}
on_save="save_row"
on_cancel="cancel_edit"
/>Attributes
on_save(:string) -phx-clickevent fired when the Save button is clicked. Receivesphx-value-idset torow_id.Defaults to
"save_row".on_cancel(:string) -phx-clickevent fired when the Cancel button is clicked. Receivesphx-value-idset torow_id.Defaults to
"cancel_edit".row_id(:string) (required) - Row identifier sent asphx-value-idon both Save and Cancel clicks.saving(:boolean) - Whentrue, the Save button is dimmed and pointer-events are disabled. Defaults tofalse.class(:string) - Additional CSS classes for the<td>. Defaults tonil.