Claude
Skills
Sign in
Back

phoenix-views-templates

Included with Lifetime
$97 forever

Render views and templates in Phoenix using HEEx templates, function components, slots, and assigns

Image & Video

What this skill does


# Phoenix Views and Templates

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.

## View Module Structure

Phoenix view modules use the `embed_templates` macro to load HEEx templates from a directory:

```elixir
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

### Basic Template Structure

HEEx templates combine HTML with embedded Elixir expressions:

```heex
<section>
  <h2>Hello World, from Phoenix!</h2>
</section>
```

### Interpolating Dynamic Content

Use `<%= ... %>` to interpolate Elixir expressions into HTML:

```heex
<section>
  <h2>Hello World, from <%= @messenger %>!</h2>
</section>
```

The `@` symbol accesses assigns passed from the controller.

### Multi-line Expressions

For expressions without output, omit the `=`:

```heex
<% # This is a comment %>
<% user_name = String.upcase(@user.name) %>
<p>Welcome, <%= user_name %>!</p>
```

## Working with Assigns

Assigns are key-value pairs passed from controllers to templates:

```elixir
# Controller
def show(conn, %{"messenger" => messenger}) do
  render(conn, :show, messenger: messenger, receiver: "Dweezil")
end
```

```heex
<!-- Template -->
<section>
  <h2>Hello <%= @receiver %>, from <%= @messenger %>!</h2>
</section>
```

All assigns are accessed with the `@` prefix in templates.

## Conditional Rendering

### Using if/else

HEEx supports conditional rendering with `if/else` blocks:

```heex
<%= if some_condition? do %>
  <p>Some condition is true for user: <%= @username %></p>
<% else %>
  <p>Some condition is false for user: <%= @username %></p>
<% end %>
```

### Using unless

For negative conditions:

```heex
<%= unless @user.premium do %>
  <div class="upgrade-banner">
    Upgrade to premium for more features!
  </div>
<% end %>
```

### Pattern Matching with case

For multiple conditions:

```heex
<%= 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 %>
```

## Looping and Iteration

### For Comprehensions

Generate dynamic lists using `for`:

```heex
<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>
```

### Iterating Over Collections

Loop through lists or maps:

```heex
<ul>
  <%= for post <- @posts do %>
    <li>
      <h3><%= post.title %></h3>
      <p><%= post.excerpt %></p>
    </li>
  <% end %>
</ul>
```

### Shorthand :for Attribute

HEEx provides cleaner syntax for simple iterations:

```heex
<ul>
  <li :for={item <- @items}><%= item.name %></li>
</ul>
```

### Accessing Index

Get the iteration index with `Enum.with_index/2`:

```heex
<%= for {item, index} <- Enum.with_index(@items) do %>
  <div class="item-<%= index %>">
    <%= item.name %>
  </div>
<% end %>
```

## Function Components

Function components are reusable UI elements defined as Elixir functions that return HEEx templates.

### Defining Function Components

Use the `attr` macro to declare attributes and the `~H` sigil for the template:

```elixir
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
```

### Using Function Components

Invoke components with the `<.component_name />` syntax:

```heex
<section>
  <.greet messenger={@messenger} />
</section>
```

### Optional Attributes with Defaults

Define optional attributes with default values:

```elixir
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
```

### Multiple Attribute Types

Components can accept various attribute types:

```elixir
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
```

### Components with Computed Values

Use `assign/2` to compute values within components:

```elixir
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

Slots allow components to accept blocks of content, enabling powerful composition patterns.

### Defining and Using Slots

Define a slot and render it:

```elixir
slot :inner_block, required: true

def card(assigns) do
  ~H"""
  <div class="card">
    <%= render_slot(@inner_block) %>
  </div>
  """
end
```

Use the component with content:

```heex
<.card>
  <h2>Card Title</h2>
  <p>Card content goes here</p>
</.card>
```

### Named Slots

Components can have multiple named slots:

```elixir
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:

```heex
<.panel>
  <:header>
    <h2>Panel Title</h2>
  </:header>
  <:body>
    <p>Panel content</p>
  </:body>
  <:footer>
    <button>Close</button>
  </:footer>
</.panel>
```

### Slots with Attributes

Slots can accept attributes for more dynamic rendering:

```elixir
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
```

## Rendering Child Templates

### Rendering Other Templates

Include child templates within a parent:

```heex
<%= render("child_template.html", assigns) %>
```

### Rendering Components from Other Modules

Call components from different modules:

```heex
<MyApp.Components.button text="Click me" />
```

Or with aliasing:

```elixir
alias MyApp.Components

# In template:
<Components.button text="Click me" />
```

## Layout Templates

### Using Layouts

Layouts wrap rendered templates. Configure the layout in the controller:

```elixir
def controller do
  quote do
    use Phoenix.Controller,
      formats: [:html, :json],
      layouts: [html: HelloWeb.Layouts]
    ...
  end
end
```

### Root Layout

The root layout includes the `@inner_content` placeholder:

```heex
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8"/>
    <title>My App</title>
  </head>
  <body>
    <%= @inner_content %>
  </body>
</html>
```

### App Layout Component

Nest layouts using components:

```heex
<Layouts.app flash={@flash}>
  <section>
    <h2>Hello World, from <%= @messenger %>!</h2>
  </section>
</Layouts.app>
```

### Disabling Layouts

Render without a layout:

```elixir
def home(conn, _params) 

Related in Image & Video