Skills liveview-code-review
Reviews Phoenix LiveView code for lifecycle patterns, assigns/streams usage, components, and security. Use when reviewing LiveView modules, .heex templates, or LiveComponents.
install
source · Clone the upstream repo
git clone https://github.com/openclaw/skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/openclaw/skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/anderskev/liveview-code-review" ~/.claude/skills/clawdbot-skills-liveview-code-review && rm -rf "$T"
manifest:
skills/anderskev/liveview-code-review/SKILL.mdsource content
LiveView Code Review
Quick Reference
| Issue Type | Reference |
|---|---|
| mount, handle_params, handle_event, handle_async | references/lifecycle.md |
| When to use assigns vs streams, AsyncResult | references/assigns-streams.md |
| Function vs LiveComponent, slots, attrs | references/components.md |
| Authorization per event, phx-value trust | references/security.md |
Review Checklist
Critical Issues
- No socket copying into async functions (extract values first)
- Every handle_event validates authorization
- No sensitive data in assigns (visible in DOM)
- phx-value data is validated (user-modifiable)
Lifecycle
- Subscriptions wrapped in
connected?(socket) - handle_params used for URL-based state
- handle_async handles :loading and :error states
Data Management
- Streams used for large collections (100+ items)
- temporary_assigns for data not needed after render
- AsyncResult patterns for loading states
Components
- Function components preferred over LiveComponents
- LiveComponents preserve :inner_block in update/2
- Slots use proper attr declarations
- phx-debounce on text inputs
Valid Patterns (Do NOT Flag)
- Empty mount returning {:ok, socket} - Valid for simple LiveViews
- Using assigns for small lists - Streams only needed for 100+ items
- LiveComponent without update/2 - Default update/2 assigns all
- phx-click without phx-value - Event may not need data
- Inline function in heex - Valid for simple transforms
Context-Sensitive Rules
| Issue | Flag ONLY IF |
|---|---|
| Missing debounce | Input is text/textarea AND triggers server event |
| Use streams | Collection has 100+ items OR is paginated |
| Missing auth check | Event modifies data AND no auth in mount |
Critical Anti-Patterns
Socket Copying (MOST IMPORTANT)
# BAD - socket copied into async function def handle_event("load", _, socket) do Task.async(fn -> user = socket.assigns.user # Socket copied! fetch_data(user.id) end) {:noreply, socket} end # GOOD - extract values first def handle_event("load", _, socket) do user_id = socket.assigns.user.id Task.async(fn -> fetch_data(user_id) # Only primitive copied end) {:noreply, socket} end
Missing Authorization
# BAD - trusts phx-value without auth def handle_event("delete", %{"id" => id}, socket) do Posts.delete_post!(id) # Anyone can delete any post! {:noreply, socket} end # GOOD - verify authorization def handle_event("delete", %{"id" => id}, socket) do post = Posts.get_post!(id) if post.user_id == socket.assigns.current_user.id do Posts.delete_post!(post) {:noreply, stream_delete(socket, :posts, post)} else {:noreply, put_flash(socket, :error, "Unauthorized")} end end
Before Submitting Findings
Use the issue format:
[FILE:LINE] ISSUE_TITLE for each finding.
Load and follow review-verification-protocol before reporting any issue.