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.
npx claudepluginhub oliver-kriska/claude-elixir-phoenix --plugin elixir-phoenixThis skill uses the workspace's default tool permissions.
Reference for building with Phoenix LiveView 1.0/1.1.
Generates design tokens/docs from CSS/Tailwind/styled-components codebases, audits visual consistency across 10 dimensions, detects AI slop in UI.
Records polished WebM UI demo videos of web apps using Playwright with cursor overlay, natural pacing, and three-phase scripting. Activates for demo, walkthrough, screen recording, or tutorial requests.
Delivers idiomatic Kotlin patterns for null safety, immutability, sealed classes, coroutines, Flows, extensions, DSL builders, and Gradle DSL. Use when writing, reviewing, refactoring, or designing Kotlin code.
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 auth