---
trigger: glob
globs: lib/**/*.ex,lib/**/*.heex,test/**/*.exs
---

# Phoenix LiveView

- Commands: `mix format --check-formatted`, `mix compile --warnings-as-errors`, `mix test`, `mix credo` if present. Check `mix.exs` for Phoenix/LiveView versions.
- LiveViews and components live in `lib/my_app_web/`; business logic and Repo calls stay in contexts under `lib/my_app/`. A LiveView never calls `Repo` directly.
- `mount/3` sets initial assigns cheaply; load data that needs the connection only when `connected?(socket)` if it's expensive, or use `assign_async`/`start_async` for slow work.
- Large or growing collections use streams (`stream(socket, :items, items)`, `stream_insert`, `stream_delete`) with `phx-update="stream"` and DOM ids, not lists in assigns.
- Forms: build with `to_form(changeset)`, render with `<.form for={@form}>` and `<.input field={@form[:name]}>`; validate on `phx-change`, save on `phx-submit`.
- Reusable UI as function components with `attr`/`slot` declarations (`core_components.ex`); use `live_component` only when it needs its own state and events.
- Navigation with `<.link navigate={~p"/path"}>` / `push_navigate` and verified routes (`~p`); `push_patch` for same-LiveView param changes, handled in `handle_params/3`.
- Real-time updates via `Phoenix.PubSub` broadcasts from the context after successful writes; subscribe in `mount` when connected.
- Authorization in `on_mount` hooks and again in each `handle_event` that changes data; never trust params for ownership.
- Use `phx-debounce` on text inputs; keep events small and explicit (`phx-click="delete" phx-value-id={item.id}`).

## Tests
`Phoenix.LiveViewTest`: `live/2`, `render_click`, `render_submit`, `has_element?` with selectors; context tests for business rules.
