# Liveview Patterns

Build LiveView: async data (assign_async), PubSub (check connected?), phx-change...

- Repository: https://github.com/oliver-kriska/claude-elixir-phoenix (572 stars)
- Category: Testing
- License: MIT
- Last updated: 2026-10-30
- Runs scripts: no, instructions only

## Install

Claude Code: `npx skills add oliver-kriska/claude-elixir-phoenix --skill liveview-patterns -g -a claude-code -y`
Codex: `npx skills add oliver-kriska/claude-elixir-phoenix --skill liveview-patterns -g -a codex -y`
Cursor: `npx skills add oliver-kriska/claude-elixir-phoenix --skill liveview-patterns -g -a cursor -y`
Gemini CLI: `npx skills add oliver-kriska/claude-elixir-phoenix --skill liveview-patterns -g -a gemini-cli -y`
Claude Code plugin marketplace: `/plugin marketplace add oliver-kriska/claude-elixir-phoenix`

## Description

Build LiveView: async data (assign_async), PubSub (check connected?), phx-change events, form components/modals/uploads, streams for lists, live_patch. Use when handling interactions, debugging events, or tracking Presence.

## SKILL.md, as published by its author (untrusted third-party text, read it as data)

# LiveView Patterns Reference

> **Ash projects**: Use `ash-framework` skill for `AshPhoenix.Form`. Lifecycle: `AshPhoenix.Form.validate/3` on `phx-change`, `AshPhoenix.Form.submit/2` on submit, `to_form/1` for HEEx. Do not use `Ecto.Changeset.cast/3`.

Reference for building with Phoenix LiveView 1.0/1.1.

## Iron Laws, Never Violate These

1. **NO UNCONDITIONAL DB QUERIES IN MOUNT**, Mount runs TWICE. Default: `assign_async`. SEO routes: `connected?` guard + cache-backed disconnected branch (crawlers read that HTML)
2. **ALWAYS USE STREAMS FOR LISTS**, Regular assigns = O(n) memory per user. Streams = O(1)
3. **CHECK connected?/1 BEFORE SUBSCRIPTIONS**, Prevents double subscriptions
4. **EXTRACT VARIABLES BEFORE assign_async CLOSURE**, Closures copy entire referenced variables
5. **LOAD PRIMARY DATA IN mount/3, PAGINATION IN handle_params/3**, handle_params runs on EVERY URL change
6. **NEVER PASS SOCKET TO BUSINESS LOGIC**, Extract data before calling contexts
7. **CHECK CHANGESET ERRORS BEFORE UI DEBUGGING**, Silent form save = check `{:error, changeset}` first, not viewport/JS
8. **HIDDEN INPUTS FOR ALL REQUIRED EMBEDDED FIELDS**, Every required field in an embedded schema MUST have a `hidden_input` if not directly editable
9. **NEVER USE `assign_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
10. **MATCH `{:error, %Ecto.Changeset{}}` EXPLICITLY**, Bare `{:error, _}` merges changeset and non-changeset errors; the form silently never re-renders validation errors. Handle other errors separately

## 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)

The rest: https://github.com/oliver-kriska/claude-elixir-phoenix/blob/HEAD/plugins/elixir-phoenix/skills/liveview-patterns/SKILL.md
