From elixir-phoenix
References Phoenix LiveView patterns for PubSub, uploads, components, forms, assign_async, streams. Use when building LiveView features or debugging handle_event lifecycle.
How this skill is triggered — by the user, by Claude, or both
Slash command
/elixir-phoenix:liveview-patternsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Reference for building with Phoenix LiveView 1.0/1.1.
Reference for building with Phoenix LiveView 1.0/1.1.
assign_async{:error, changeset} first, not viewport/JShidden_input if not directly editableassign_new FOR LIFECYCLE VALUES — assign_new skips the function if key exists. Use assign/3 for locale, current user, or any value refreshed every mount| Pattern | 3K items | 10K users × 10K items |
|---|---|---|
| Regular assigns | ~5.1 MB | ~10+ GB |
| Streams | ~1.1 MB | Minimal (O(1)) |
Decision: Lists with >100 items → Use streams, not assigns
def mount(%{"slug" => slug}, _session, socket) do
# Extract needed values BEFORE the closure
scope = socket.assigns.current_scope
{:ok,
socket
|> assign_async(:org, fn -> {:ok, %{org: fetch_org(scope, slug)}} end)}
end
def mount(_params, _session, socket) do
{:ok, stream(socket, :items, Items.list_items())}
end
# Insert/update/delete
stream_insert(socket, :items, item, at: 0)
stream_delete(socket, :items, item)
def mount(_params, _session, socket) do
if connected?(socket), do: Chat.subscribe(room_id)
{:ok, socket}
end
Same LiveView, different params? → patch / push_patch
Different LiveView, same live_session? → navigate / push_navigate
Different live_session or non-LiveView? → href / redirect
Does component need BOTH internal state AND event handling?
│
├── YES → Does it encapsulate APPLICATION logic (not just DOM)?
│ ├── YES → Use LiveComponent ✅
│ └── NO → Refactor to function component with parent handling
│
└── NO → Use Function Component ✅
Official guidance: "Prefer function components over live components"
| Wrong | Right |
|---|---|
DB queries without assign_async | Use assign_async for all queries |
assign(socket, items: list) for lists | stream(socket, :items, list) |
PubSub subscribe without connected? | if connected?(socket), do: subscribe() |
| Passing socket to context functions | Extract socket.assigns first |
Business logic in handle_event | Delegate to context |
assign_new for locale/user in hooks | assign/3 (must run every mount) |
For detailed patterns, see:
${CLAUDE_SKILL_DIR}/references/async-streams.md - assign_async, stream_async, streams${CLAUDE_SKILL_DIR}/references/forms-uploads.md - Forms, validation, file uploads${CLAUDE_SKILL_DIR}/references/components.md - Function components, LiveComponents${CLAUDE_SKILL_DIR}/references/pubsub-navigation.md - PubSub, navigation, JS commands${CLAUDE_SKILL_DIR}/references/js-interop.md - Third-party JS libraries, phx-update="ignore", hooks${CLAUDE_SKILL_DIR}/references/channels-presence.md - Phoenix Channels, Presence, token authnpx claudepluginhub oliver-kriska/claude-elixir-phoenix --plugin elixir-phoenixProvides Phoenix LiveView best practices: no DB queries in mount (called twice), load data in handle_params, security scopes, scoped PubSub topics, GenServer polling, async assigns, and gotchas.
Enforces Phoenix LiveView best practices: @impl true callbacks, assign initialization in mount/handle_params, connected? checks, proper tuples, and two-phase rendering awareness. Invoke before LiveView modules or .heex templates.
Guides Phoenix web app development including LiveView, contexts, channels, and production runtime configuration.