Expert guidance for Phoenix web applications with LiveView, Ecto, and Elixir best practices
This skill has safety concerns that you should review before use. Some patterns were detected that may pose a risk.Safety score: 60/100.
KillerSkills scans all public content for safety. Use caution before installing or executing flagged content.
This skill provides expert guidance for developing Phoenix web applications, covering Phoenix v1.8+, LiveView, Ecto, authentication, and Elixir best practices.
Assists with Phoenix web application development by providing context-aware guidance on:
1. **Always** run `mix precommit` when finishing changes to catch issues before committing
2. Fix any pending issues reported by the precommit check
1. **Always** begin LiveView templates with `<Layouts.app flash={@flash} ...>` wrapping all inner content
2. The `MyAppWeb.Layouts` module is aliased in `my_app_web.ex` - use it without additional aliasing
3. **Always** use the imported `<.input>` component from `core_components.ex` for form inputs
4. Use `<.icon name="hero-x-mark" class="w-5 h-5"/>` component for hero icons
5. **Never** call `<.flash_group>` outside of the `layouts.ex` module
6. When overriding input classes, provide complete styling as no defaults are inherited
1. **Always** handle authentication at the router level with proper redirects
2. Understand the authentication scopes:
- `:fetch_current_user` plug - included in default browser pipeline
- `:require_authenticated_user` plug - redirects unauthenticated users to login
- `live_session :current_user` - for routes needing current user without requiring auth
- `live_session :require_authenticated_user` - for routes requiring authentication
- `redirect_if_user_is_authenticated` plug - redirects authenticated users away from signup/login
3. **Always** access current user via `@current_scope.user`, never `@current_user`
4. **Never** duplicate `live_session` names - define each once and group all routes in a single block
5. **Always** explain which router scope, `live_session`, and pipeline you're using and why
**Routes requiring authentication:**
```elixir
scope "/", AppWeb do
pipe_through [:browser, :require_authenticated_user]
live_session :require_authenticated_user,
on_mount: [{GameMasterCoreWeb.UserAuth, :require_authenticated}] do
live "/users/settings", UserLive.Settings, :edit
live "/dashboard", DashboardLive, :index
end
end
```
**Routes working with or without authentication:**
```elixir
scope "/", MyAppWeb do
pipe_through [:browser]
live_session :current_user,
on_mount: [{GameMasterCoreWeb.UserAuth, :mount_current_scope}] do
live "/", PublicLive
end
end
```
1. **Lists and Index Access:**
- **Never** use bracket syntax `mylist[i]` (invalid in Elixir)
- **Always** use `Enum.at(mylist, i)`, pattern matching, or `List` module
2. **Variable Binding in Block Expressions:**
- Variables are immutable but can be rebound
- **Always** bind the result of `if`, `case`, `cond` blocks to use them:
```elixir
# INVALID
if connected?(socket) do
socket = assign(socket, :val, val)
end
# VALID
socket =
if connected?(socket) do
assign(socket, :val, val)
end
```
3. **Module Organization:**
- **Never** nest multiple modules in the same file (causes cyclic dependencies)
4. **Struct Access:**
- **Never** use map access syntax on structs: `changeset[:field]`
- **Always** use direct field access: `my_struct.field`
- For changesets: `Ecto.Changeset.get_field(changeset, :field)`
5. **Date/Time:**
- Use built-in `Time`, `Date`, `DateTime`, and `Calendar` modules
- Only add dependencies for parsing (use `date_time_parser` if needed)
6. **Naming:**
- Predicate functions should end with `?`, not start with `is_`
- Reserve `is_thing` for guards
7. **Concurrency:**
- Use `Task.async_stream(collection, callback, timeout: :infinity)` for concurrent enumeration with back-pressure
1. Be mindful of `scope` block aliases - they prefix all routes within
2. **Never** create redundant aliases - the scope provides them:
```elixir
scope "/admin", AppWeb.Admin do
pipe_through :browser
live "/users", UserLive, :index # Points to AppWeb.Admin.UserLive
end
```
3. **Never** use deprecated `Phoenix.View`
1. **Always** preload associations when they'll be accessed in templates
2. Remember to `import Ecto.Query` in `seeds.exs`
3. Schema fields always use `:string` type, even for `:text` columns
4. `validate_number/2` does **not** support `:allow_nil` option
5. **Must** use `Ecto.Changeset.get_field(changeset, :field)` to access fields
6. Fields set programmatically (like `user_id`) must not be in `cast` calls
1. **Always** use `~H` or `.html.heex` files, **never** `~E`
2. **Always** use imported `Phoenix.Component.form/1` and `to_form/2`
3. **Never** use deprecated `Phoenix.HTML.form_for`
4. **Always** add unique DOM IDs to key elements for testing
5. **Conditionals:**
- Elixir does **not** support `else if` or `elsif`
- **Always** use `cond` or `case` for multiple conditions:
```heex
<%= cond do %>
<% condition1 -> %>
...
<% condition2 -> %>
...
<% true -> %>
...
<% end %>
```
6. **Literal Curly Braces:**
- Use `phx-no-curly-interpolation` for code snippets:
```heex
<code phx-no-curly-interpolation>
let obj = {key: "val"}
</code>
```
7. **Class Attributes:**
- **Always** use list syntax for multiple/conditional classes:
```heex
<a class={[
"px-2 text-white",
@some_flag && "py-5",
if(@condition, do: "border-red-500", else: "border-blue-100")
]}>Text</a>
```
8. **Comments:**
- **Always** use HEEx comment syntax: `<%!-- comment --%>`
9. **Interpolation:**
- Use `{...}` for tag attributes and simple values in bodies
- Use `<%= ... %>` for block constructs (if, cond, case, for) in bodies:
```heex
<div id={@id}>
{@my_assign}
<%= if @condition do %>
{@another_assign}
<% end %>
</div>
```
10. **Never** use `<% Enum.each %>` for templates - **always** use `<%= for item <- @collection do %>`
1. **Never** use deprecated `live_redirect`/`live_patch`
2. **Always** use `<.link navigate={href}>`, `<.link patch={href}>`, `push_navigate`, `push_patch`
3. **Avoid** LiveComponents unless specifically needed
4. Name LiveViews with `Live` suffix: `AppWeb.WeatherLive`
5. When using `phx-hook="MyHook"`, **must** also set `phx-update="ignore"`
6. **Never** write embedded `<script>` tags - use `assets/js` directory
1. **Always** use streams for collections to avoid memory issues:
- Append: `stream(socket, :messages, [new_msg])`
- Reset: `stream(socket, :messages, [new_msg], reset: true)`
- Prepend: `stream(socket, :messages, [new_msg], at: -1)`
- Delete: `stream_delete(socket, :messages, msg)`
2. In templates:
- Set `phx-update="stream"` on parent element
- Add DOM id on parent: `id="messages"`
- Consume the stream properly in the template
**Example 1: Adding an authenticated route**
```elixir
live "/projects", ProjectLive.Index, :index
```
**Example 2: Accessing current user in template**
```heex
<div>Welcome, {@current_scope.user.email}</div>
```
**Example 3: Proper list access**
```elixir
Enum.at(mylist, 0)
```
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/phoenix-framework-development-assistant/raw