View
- How to pass variables to template?
- How to handle lists?
- How to handle branching?
- How to escape dangerous content?
Passing Variables
Take a look at lib/app1_web/controllers/page_html/home.html.heex. Replace it with following content:
<div>{ @message }</div>
Variables can be passed directly to render:
render(conn, :home, layout: false, message: "Hello")
You can also assign variables to conn:
conn
|> assign(:user, %{:name => "Alice"})
|> render(:home, layout: false)
<div>{@user.name}</div>
It is worth mentioning that assign is imported from Plug.Conn and it's immutable in behavior.
Render a list
<ul>
<%= for user <- @users do %>
<li>{user.name}</li>
<% end %>
</ul>
Or use the :for attribute, which is more concise:
<ul :for={user <- @users}>
<li>{user.name}</li>
</ul>
Branching
<%= if @logged_in do %>
<p>Welcome back!</p>
<% else %>
<p>Please log in.</p>
<% end %>
Elixir is more expressive than average programming languages, and it
offers case and cond:
<%= case @user.role do %>
<% "admin" -> %>
<p>You're an admin.</p>
<% "member" -> %>
<p>You're a member.</p>
<% _ -> %>
<p>Unknown role.</p>
<% end %>
<%= cond do %>
@user.age < 13 ->
<p>You're a child.</p>
@user.age < 20 ->
<p>You're a teenager.</p>
true ->
<p>You're an adult.</p>
<% end %>
Escaping
Dangerous or untrusted content (like user input) must be escaped to prevent XSS attacks.
Phoenix's HEEx templates automatically escape content by default.
To render trusted content without excapping:
<%= raw("<b>Check you console!</b>") %>
<%= raw("<script>console.log('code executing!')</script>") %>
Layouts
Layouts are a way to define a shared HTML structure (like headers, navbars, footers) that wraps around your templates.
In lib/app1_web/components/layouts.ex, embed_templates is called
to embed the template under the layouts folder as functions.
To render with the default layout:
render(conn, :home)
Custom Layout
Create a custom layout at lib/app1_web/components/layouts/admin.html.heex.
<header><h1>Admin Panel</h1></header>
<main><%= @inner_content %></main>
<footer><p>© 2025 Demo</p></footer>
The custom layout will also be wrapped with the root layout. So no need for a full html structure.
Specify the layout with put_layout:
conn
|> put_layout(html: :admin)
|> render(:home)
Component
<.button>Submit</.button>
Define custom component:
defmodule App1Web.CoreComponents do
use Phoenix.Component
def alert(assigns) do
~H"""
<div class={"alert alert-#{@type || "info"}"}>
<%= render_slot(@inner_block) %>
</div>
"""
end
end
<.alert type="error"><div>Something went wrong!</div></.alert>