The simplest option would be to pass the page title along with the render call in your controller action. For example you could do render(conn, “new.html”, page_title: “New Resource”). Then, in your app layout template app.html.eex, access it with <title><%= @page_title %></title>.

The problem with this approach is that you have to provide the page_title for every controller action. In addition setting the page title should probably be done by the view and not the controller. Here is how you’d do that.

Start out by adding phoenix controller’s action_name/1 to the imported helpers for our view in web.ex. action_name/1 returns the action as atom that is being performed for the current conn (:new, :create, :update, …).

# web/web.ex
def view do
  # Import convenience functions from controllers
  import Phoenix.Controller, only: [get_csrf_token: 0, get_flash: 2, view_module: 1, action_name: 1]
  ...
end

Next up create a title/2 function in our LayoutView module. This function will try and ask the view module being rendered for its title or return a default title instead.

# web/views/layout_view.ex
@doc """
Calls the `title` fun of the current `view_module` with the performed `:action` as arg.
If no fun exists/matches the `default` title is returned instead.
"""
def title(conn, default) do
  try do
    apply(view_module(conn), :title, [action_name(conn)])
  rescue
    _ -> default
  end
end

Using view_module(conn) we fetch the module of the view which is being rendered, for example the UserView. apply/3 will try to call the function :title inside that module with the action name as argument.

If that view does not implement title/1 a UndefinedFunctionError error is thrown. We don’t really care about the kind of the error so in our rescue block we just return the default title instead.

Now it is time to implement the title/1 function in all views we want to specify a custom title for. In this example I assume you have a user model with matching user controller and a user view.

# web/views/user_view.ex
def title(:index), do: "List all users"
def title(:new),   do: "Create a new user"
def title(:show),  do: "Show a single user"
def title(_),      do: "Users"

We use elixir’s pattern matching to specify a custom title for every controller action. If you only need one title for all actions simply use def title(_), which will always match.

That’s it. Open the layout template at web/templates/layout/app.html.eex and add the call to title/2.

# web/templates/layout/app.html.eex
<head>
  ...
  <title><%= title @conn, "My Default Title" %></title>
  ...
</head>

Now title @conn, “My Default Title” will call title/2 defined in our web/views/layout_view.ex, which in turn via apply/3 calls the title/1 function of the current view module that is being rendered. If no function matches or none is found “My Default Title” is used instead.

Subscribe and get mail when I post.

Sending plain text and HTML emails using EEx and Swoosh in Phoenix & Elixir
Itror - In The Right Order - Launch Day