Inline-editable field component — click to edit, Enter to submit, Escape to cancel.
Follows the shadcn/ui "Editable" pattern adapted for Phoenix LiveView. The component
renders in two modes controlled entirely by the PhiaEditable JavaScript hook:
- Preview mode — shows slot content (or placeholder). A click or keyboard activation switches to edit mode.
- Edit mode — shows the
:inputslot (an<input>or<textarea>).Entersubmits the value viapushEvent/3;Escapecancels without saving. Clicking outside the element also cancels.
Zero external JS dependencies — the hook is vanilla JS.
Usage
<.editable id="title-edit" value={@title} on_submit="update_title" placeholder="Click to edit">
<:preview>{@title}</:preview>
<:input>
<input type="text" id="title-edit-input" name="title" value={@title}
class="w-full border rounded px-2 py-1" />
</:input>
</.editable>In your LiveView, handle the submitted value:
def handle_event("update_title", %{"value" => value}, socket) do
{:noreply, assign(socket, title: value)}
endAttributes
| Attribute | Type | Default | Description |
|---|---|---|---|
id | string | required | Unique element ID (required by the hook) |
value | string | nil | Current value (for reference; not rendered) |
placeholder | string | "Click to edit" | Text shown when preview slot is empty |
on_submit | string | nil | LiveView event pushed when user submits |
class | string | nil | Extra classes merged onto the container div |
Slots
| Slot | Required | Description |
|---|---|---|
preview | yes | Content rendered in preview (read) mode |
input | yes | Content rendered in edit mode (typically <input>) |
Accessibility
- The preview div has
role="button"andtabindex="0"so keyboard users can activate it withEnterorSpace. aria-label="Click to edit"provides a descriptive accessible name.- The edit-mode input receives focus automatically on activation.
JS Hook: PhiaEditable
Register the hook exported from priv/templates/js/hooks/editable.js:
import PhiaEditable from "../../../deps/phia_ui/priv/templates/js/hooks/editable"
let liveSocket = new LiveSocket("/live", Socket, {
hooks: { PhiaEditable }
})
Summary
Functions
Renders an inline-editable field.
Functions
Renders an inline-editable field.
The component shows the :preview slot by default. Clicking (or pressing
Enter/Space) on the preview activates edit mode and shows the :input
slot. The PhiaEditable JS hook manages the mode toggle, focus, keyboard
handling, and event dispatch.
Example
<.editable id="bio-edit" value={@bio} on_submit="update_bio" placeholder="Add a bio…">
<:preview>{@bio}</:preview>
<:input>
<textarea id="bio-edit-input" name="bio" rows="3"
class="w-full border rounded px-2 py-1"><%= @bio %></textarea>
</:input>
</.editable>Attributes
id(:string) (required) - Unique element ID required by the PhiaEditable hook.value(:string) - Current value of the field. Passed to the component for reference. Defaults tonil.placeholder(:string) - Placeholder text shown when the preview slot renders no content. Defaults to"Click to edit".on_submit(:string) - LiveView event name pushed when the user submits the new value. Omit to disable submission. Defaults tonil.class(:string) - Additional CSS classes merged onto the container div viacn/1. Defaults tonil.
Slots
preview(required) - Content displayed in preview (read) mode. May be plain text or rich HTML.input(required) - Content displayed in edit mode. Should contain an<input>or<textarea>.