typescript-utility-types
Use when typeScript utility types, mapped types, and advanced type manipulation. Use when creating flexible, type-safe TypeScript code.
phoenix-views-templates
Render views and templates in Phoenix using HEEx templates, function components, slots, and assigns
Full skill instructions
Phoenix uses HEEx (HTML+EEx) templates for rendering dynamic HTML content. HEEx provides compile-time validation, security through automatic escaping, and a component-based architecture. Views in Phoenix are modules that organize template rendering logic and house reusable function components.
Phoenix view modules use the embed_templates macro to load HEEx templates from a directory:
defmodule HelloWeb.HelloHTML do
use HelloWeb, :html
embed_templates "hello_html/*"
end
This automatically creates functions for each .html.heex file in the hello_html/ directory.
HEEx templates combine HTML with embedded Elixir expressions:
<section>
<h2>Hello World, from Phoenix!</h2>
</section>
Use <%= ... %> to interpolate Elixir expressions into HTML:
<section>
<h2>Hello World, from <%= @messenger %>!</h2>
</section>
The @ symbol accesses assigns passed from the controller.
For expressions without output, omit the =:
<% # This is a comment %>
<% user_name = String.upcase(@user.name) %>
<p>Welcome, <%= user_name %>!</p>
Assigns are key-value pairs passed from controllers to templates:
# Controller
def show(conn, %{"messenger" => messenger}) do
render(conn, :show, messenger: messenger, receiver: "Dweezil")
end
<!-- Template -->
<section>
<h2>Hello <%= @receiver %>, from <%= @messenger %>!</h2>
</section>
All assigns are accessed with the @ prefix in templates.
HEEx supports conditional rendering with if/else blocks:
<%= if some_condition? do %>
<p>Some condition is true for user: <%= @username %></p>
<% else %>
<p>Some condition is false for user: <%= @username %></p>
<% end %>
For negative conditions:
<%= unless @user.premium do %>
<div class="upgrade-banner">
Upgrade to premium for more features!
</div>
<% end %>
For multiple conditions:
<%= case @status do %>
<% :pending -> %>
<span class="badge badge-warning">Pending</span>
<% :approved -> %>
<span class="badge badge-success">Approved</span>
<% :rejected -> %>
<span class="badge badge-danger">Rejected</span>
<% end %>
Generate dynamic lists using for:
<table>
<tr>
<th>Number</th>
<th>Power</th>
</tr>
<%= for number <- 1..10 do %>
<tr>
<td><%= number %></td>
<td><%= number * number %></td>
</tr>
<% end %>
</table>
Loop through lists or maps:
<ul>
<%= for post <- @posts do %>
<li>
<h3><%= post.title %></h3>
<p><%= post.excerpt %></p>
</li>
<% end %>
</ul>
HEEx provides cleaner syntax for simple iterations:
<ul>
<li :for={item <- @items}><%= item.name %></li>
</ul>
Get the iteration index with Enum.with_index/2:
<%= for {item, index} <- Enum.with_index(@items) do %>
<div class="item-<%= index %>">
<%= item.name %>
</div>
<% end %>
Function components are reusable UI elements defined as Elixir functions that return HEEx templates.
Use the attr macro to declare attributes and the ~H sigil for the template:
defmodule HelloWeb.HelloHTML do
use HelloWeb, :html
embed_templates "hello_html/*"
attr :messenger, :string, required: true
def greet(assigns) do
~H"""
<h2>Hello World, from <%= @messenger %>!</h2>
"""
end
end
Invoke components with the <.component_name /> syntax:
<section>
<.greet messenger={@messenger} />
</section>
Define optional attributes with default values:
attr :messenger, :string, default: nil
attr :class, :string, default: "greeting"
def greet(assigns) do
~H"""
<h2 class={@class}>
Hello World<%= if @messenger, do: ", from #{@messenger}" %>!
</h2>
"""
end
Components can accept various attribute types:
attr :title, :string, required: true
attr :count, :integer, default: 0
attr :active, :boolean, default: false
attr :user, :map, required: true
attr :items, :list, default: []
def card(assigns) do
~H"""
<div class={"card" <> if @active, do: " active", else: ""}>
<h3><%= @title %></h3>
<p>Count: <%= @count %></p>
<p>User: <%= @user.name %></p>
<ul>
<li :for={item <- @items}><%= item %></li>
</ul>
</div>
"""
end
Use assign/2 to compute values within components:
attr :x, :integer, required: true
attr :y, :integer, required: true
attr :title, :string, required: true
def sum_component(assigns) do
assigns = assign(assigns, sum: assigns.x + assigns.y)
~H"""
<h1><%= @title %></h1>
<p>Sum: <%= @sum %></p>
"""
end
Slots allow components to accept blocks of content, enabling powerful composition patterns.
Define a slot and render it:
slot :inner_block, required: true
def card(assigns) do
~H"""
<div class="card">
<%= render_slot(@inner_block) %>
</div>
"""
end
Use the component with content:
<.card>
<h2>Card Title</h2>
<p>Card content goes here</p>
</.card>
Components can have multiple named slots:
slot :header, required: true
slot :body, required: true
slot :footer
def panel(assigns) do
~H"""
<div class="panel">
<div class="panel-header">
<%= render_slot(@header) %>
</div>
<div class="panel-body">
<%= render_slot(@body) %>
</div>
<%= if @footer != [] do %>
<div class="panel-footer">
<%= render_slot(@footer) %>
</div>
<% end %>
</div>
"""
end
Usage:
<.panel>
<:header>
<h2>Panel Title</h2>
</:header>
<:body>
<p>Panel content</p>
</:body>
<:footer>
<button>Close</button>
</:footer>
</.panel>
Slots can accept attributes for more dynamic rendering:
slot :item, required: true do
attr :title, :string, required: true
attr :highlighted, :boolean, default: false
end
def list(assigns) do
~H"""
<ul>
<%= for item <- @item do %>
<li class={if item.highlighted, do: "highlight"}>
<%= item.title %>: <%= render_slot(item) %>
</li>
<% end %>
</ul>
"""
end
Include child templates within a parent:
<%= render("child_template.html", assigns) %>
Call components from different modules:
<MyApp.Components.button text="Click me" />
Or with aliasing:
alias MyApp.Components
# In template:
<Components.button text="Click me" />
Layouts wrap rendered templates. Configure the layout in the controller:
def controller do
quote do
use Phoenix.Controller,
formats: [:html, :json],
layouts: [html: HelloWeb.Layouts]
...
end
end
The root layout includes the @inner_content placeholder:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<title>My App</title>
</head>
<body>
<%= @inner_content %>
</body>
</html>
Nest layouts using components:
<Layouts.app flash={@flash}>
<section>
<h2>Hello World, from <%= @messenger %>!</h2>
</section>
</Layouts.app>
Render without a layout:
def home(conn, _params) do
render(conn, :home, layout: false)
end
LiveView can delegate rendering to existing view modules:
defmodule AppWeb.ThermostatLive do
use Phoenix.LiveView
def render(assigns) do
Phoenix.View.render(AppWeb.PageView, "page.html", assigns)
end
end
Render LiveView components within static templates:
<h1>Temperature Control</h1>
<%= live_render(@conn, AppWeb.ThermostatLive) %>
Define and use function components in LiveView:
def weather_greeting(assigns) do
~H"""
<div title="My div" class={@class}>
<p>Hello <%= @name %></p>
<MyApp.Weather.city name="Kraków"/>
</div>
"""
end
Test views directly using render_to_string/4:
defmodule HelloWeb.ErrorHTMLTest do
use HelloWeb.ConnCase, async: true
import Phoenix.Template
test "renders 404.html" do
assert render_to_string(HelloWeb.ErrorHTML, "404", "html", []) == "Not Found"
end
test "renders 500.html" do
assert render_to_string(HelloWeb.ErrorHTML, "500", "html", []) == "Internal Server Error"
end
end
Test components in isolation:
import Phoenix.LiveViewTest
test "renders greet component" do
assigns = %{messenger: "Phoenix"}
html = rendered_to_string(~H"""
<HelloWeb.HelloHTML.greet messenger={@messenger} />
""")
assert html =~ "Hello World, from Phoenix!"
end
Use this skill when you need to:
attr macro for all component attributes:for attribute for simple iterations~p sigil in templatesraw/1 without sanitizing user inputattr macro for component attributesUse when typeScript utility types, mapped types, and advanced type manipulation. Use when creating flexible, type-safe TypeScript code.
Use when writing shell scripts that need to run across different systems, shells, or environments. Covers POSIX compatibility and platform differences.
Use when optimizing Tailwind CSS for production, reducing bundle size, and improving performance. Covers PurgeCSS, JIT mode, and build optimization.
Generate production-ready code from a Figma component or frame using the Figma MCP server
Use when integrating Dialyzer into development workflows and CI/CD pipelines for Erlang/Elixir projects.
Use when ruboCop configuration including YAML config, departments, inheritance, and customization.
Use when Elixir OTP patterns including GenServer, Supervisor, Agent, and Task. Use when building concurrent, fault-tolerant Elixir applications.
Use when creating custom Ameba rules for Crystal code analysis including rule development, AST traversal, issue reporting, and rule testing.
Use when swift's optional handling patterns including optional binding, chaining, nil coalescing, and modern approaches to safely working with optional values while avoiding common pitfalls and force unwrapping.
Use when annotating code with structured metadata, tags, and markers for AI-assisted development workflows. Covers annotation formats, semantic tags, and integration with development tools.
Use when scala collections including immutable/mutable variants, List, Vector, Set, Map operations, collection transformations, lazy evaluation with views, parallel collections, and custom collection builders for efficient data processing.
Use when creating custom React Flow nodes, edges, and handles. Covers custom node components, resizable nodes, toolbars, and advanced customization.
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgänger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience