Agent skill
liveview-patterns
LiveView patterns — PubSub, uploads, components, forms, assign_async, streams. Use when building LiveView features or debugging handle_event lifecycle.
Install this agent skill to your Project
npx add-skill https://github.com/oliver-kriska/claude-elixir-phoenix/tree/main/plugins/elixir-phoenix/skills/liveview-patterns
SKILL.md
LiveView Patterns Reference
Reference for building with Phoenix LiveView 1.0/1.1.
Iron Laws — Never Violate These
- NO DATABASE QUERIES IN DISCONNECTED MOUNT — Queries run TWICE (HTTP + WebSocket). Use
assign_async - ALWAYS USE STREAMS FOR LISTS — Regular assigns = O(n) memory per user. Streams = O(1)
- CHECK connected?/1 BEFORE SUBSCRIPTIONS — Prevents double subscriptions
- EXTRACT VARIABLES BEFORE assign_async CLOSURE — Closures copy entire referenced variables
- LOAD PRIMARY DATA IN mount/3, PAGINATION IN handle_params/3 — handle_params runs on EVERY URL change
- NEVER PASS SOCKET TO BUSINESS LOGIC — Extract data before calling contexts
- CHECK CHANGESET ERRORS BEFORE UI DEBUGGING — Silent form save = check
{:error, changeset}first, not viewport/JS - HIDDEN INPUTS FOR ALL REQUIRED EMBEDDED FIELDS — Every required field in an embedded schema MUST have a
hidden_inputif not directly editable - NEVER USE
assign_newFOR LIFECYCLE VALUES —assign_newskips the function if key exists. Useassign/3for locale, current user, or any value refreshed every mount
Memory Impact
| 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
Quick Patterns
Async Assigns (CRITICAL)
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
Streams for Lists
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)
PubSub with connected? check
def mount(_params, _session, socket) do
if connected?(socket), do: Chat.subscribe(room_id)
{:ok, socket}
end
Navigation Decision Tree
Same LiveView, different params? → patch / push_patch
Different LiveView, same live_session? → navigate / push_navigate
Different live_session or non-LiveView? → href / redirect
Component Decision Tree
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"
Common Anti-patterns
| 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) |
References
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 auth
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
lab:autoresearch
Self-improving loop for plugin skills. Reads program.md, proposes one mutation per iteration, evaluates against deterministic scorer, keeps improvements via git, reverts failures. Targets weakest skill+dimension. Use with /loop for overnight runs.
promote
Generate X/Twitter release promotion posts with ASCII tables and CodeSnap rendering. Use when writing release posts, promotion tweets, plugin announcements, or preparing social media content for new versions.
skill-monitor
Analyze skill effectiveness across sessions. Computes per-skill metrics (action rate, friction, outcomes), identifies degrading skills, and generates improvement recommendations. Requires session-scan data in metrics.jsonl.
session-trends
Analyze trends across session metrics. Computes windowed aggregates, deltas, and compares against MEMORY.md findings. Use periodically for progress tracking.
cc-changelog
CONTRIBUTOR TOOL - Track CC changelog, extract new versions since last check, analyze impact on plugin (breaking changes, opportunities, deprecations). Run periodically or before releases. NOT part of the distributed plugin.
session-scan
Compute metrics for Claude Code sessions. Discovers via ccrider, filters trivial, computes friction/opportunity/fingerprint scores. Use for broad session triage.
Didn't find tool you were looking for?