173

What is the best way to create a custom title for pages in a Rails app without using a plug-in?

Flip
  • 6,233
  • 7
  • 46
  • 75
wusher
  • 12,291
  • 22
  • 72
  • 95

17 Answers17

265

In your views do something like this:

<% content_for :title, "Title for specific page" %>
<!-- or -->
<h1><%= content_for(:title, "Title for specific page") %></h1>

The following goes in the layout file:

<head>
  <title><%= yield(:title) %></title>
  <!-- Additional header tags here -->
</head>
<body>
  <!-- If all pages contain a headline tag, it's preferable to put that in the layout file too -->
  <h1><%= yield(:title) %></h1>
</body>

It's also possible to encapsulate the content_for and yield(:title) statements in helper methods (as others have already suggested). However, in simple cases such as this one I like to put the necessary code directly into the specific views without custom helpers.

Christoph Schiessl
  • 6,818
  • 4
  • 33
  • 45
  • 28
    I merged this answer with the one below to create a "default title" for every page: `<%= (yield(:title) + " - " unless yield(:title).blank?).to_s + "This is always shown" %>` in the layout and `<% content_for :title, "Conditional title" %>` in the views. – John Jul 02 '12 at 21:21
  • 1
    Works perfect with Rails 5.2.0! – Stanimir Dimitrov May 11 '18 at 08:20
  • 2
    When I use `

    <%= content_for(:title, "Title for specific page") %>

    ` I do not see anything output into my DOM (i.e. I have `

    ` with no content in it). However my `Title for specific page` is working correctly. Any ideas?
    – JoshuaESummers Mar 24 '20 at 09:29
  • @JoshuaESummers Same here. Perhaps `content_for` has changed since the above answer was written. I had to use two separate lines: `<% content_for :title, 'The Title' %>` to set the title, followed by `

    <%= content_for :title %>

    ` to display it.
    – Matt Feb 15 '21 at 15:59
  • 2
    `<%= yield(:title).presence || 'default_title' %>` to add default title – buncis Feb 26 '22 at 07:51
  • In case you want a format of "Company - [page_title]", use: ` <%= "Company" + (" - " + yield(:page_title) unless yield(:page_title).blank?).to_s %> ` – Matteo Jul 27 '22 at 22:49
123

Here's a simple option that I like to use

In your layout

<head>
  <title><%= @title %></title>
</head>

And at the top of your page template (first line)

<% @title="Home" %>

Because of the way the layout and page templates are parsed the @title="Home" is evaluated before the layout is rendered.

opsb
  • 29,325
  • 19
  • 89
  • 99
  • 35
    It's worth noting that this won't work with rails view caching. In rails 3 content_for is smart enough to work correctly with caching (see selected answer). – opsb Nov 15 '11 at 10:41
  • 4
    shouldn't variables be set inside the controller ? –  Jun 07 '13 at 15:45
  • 2
    @SajjadMerchant generally I agree, yes variables should be set in the controller. In this case the template feels more appropriate though because you're specifying an attribute specific to the appearance of the page. It's also possible for a controller action to be used by two different views (not that this happens very often, I'm using it to illustrate how tightly bound the title is to a particular template). – opsb Jun 09 '13 at 10:35
55

Best practice is to use content_for.

First, add a couple of helper methods (ie. stick in app/helpers/application_helper.rb):

def page_title(separator = " – ")
  [content_for(:title), 'My Cool Site'].compact.join(separator)
end

def page_heading(title)
  content_for(:title){ title }
  content_tag(:h1, title)
end

Then in your layout view you can simply use:

<title><%= page_title %></title>

...and in the view itself:

<%= page_heading "Awesome" %>

This way has the advantage of allowing you to shuffle where you stick the h1 tag for your title, and keeps your controller nice and free of pesky @title variables.

Aupajo
  • 5,885
  • 6
  • 30
  • 28
  • 4
    How does one set the value of `@content_for_title`? – Jordan Warbelow-Feldstein Sep 15 '11 at 20:33
  • @JordanFeldstein I have no idea what he meant by `@content_for_title`. Should be `(content_for(:title) + ' — ' if content_for?(:title)).to_s + 'My Cool Site'`. Then to set it from your view, just `<% content_for :title, 'My Page Title' %>` – Tobias J Oct 15 '13 at 03:59
  • 1
    `@content_for_title` is what used to happen when you used `content_for`, at the time this comment was written. I believe this is no longer the case, but I haven't looked recently. Toby J's comment is correct for today's Rails. I'll update my original answer. – Aupajo Oct 31 '13 at 02:10
24

An improvement on @opsb and a more complete form of @FouZ's:

In application.html.erb:

<title><%= @title || "Default Page Title" %></title>

In the view erb file or its controller:

<% @title = "Unique Page Title" %>
arogachev
  • 33,150
  • 7
  • 114
  • 117
boulder_ruby
  • 38,457
  • 9
  • 79
  • 100
14

Look into content_for: http://railscasts.com/episodes/8

Avdi
  • 18,340
  • 6
  • 53
  • 62
7

Without further details on the use-case or requirements that you're trying to satisfy, I can think of several alternatives:

1) Switch the title in one of your layout pages and consume a helper method stored in application_helper.rb

<title><%= custom_title %></title>

This approach will give you a unique title for each layout page.

2) Railscasts suggests using a partial to load what shows up between the HEAD tags

3) Use javascript/ajax calls to manipulate the DOM if you need to change the title after the load event.

Maybe you don't really want to change the content tagged by the title element. Perhaps you really need a breadcrumb of some sort, so that your users always know where they are with respect to your site's navigation hierarchy. While I've done fine with how the goldberg plugin, I'm sure there are other ways of pulling off the same functionality.

Alan
  • 7,066
  • 5
  • 30
  • 38
6

This simple approach sets a default title, but also let you override it whenever you please.

In app/views/layouts/application.html.erb:

<title><%= yield(:title) || 'my default title' %></title>

And to override that default, place this in any view you like

<% content_for :title, "some new title" %>
stevec
  • 41,291
  • 27
  • 223
  • 311
  • 2
    You have to use `yield(:title).presence` so it returns nil if the receiver is not present. Just in case it wasn't working for anyone else. – Eduardo06sp Mar 27 '22 at 22:02
4

I use nifty_generator's "nifty_layout" which provides with a title variable which I can call then on the page using:

<% title "Title of page" %>

I can also user <% title "Title of page", false %> to have the title just show in browser title and not in the page itself.

sent-hil
  • 18,635
  • 16
  • 56
  • 74
3

You can also set it in a before_filter in your controller.

# foo_controller.rb

class FooController < ApplicationController

  before_filter :set_title

  private

  def set_title
    @page_title = "Foo Page"
  end

end

# application.html.erb

<h1><%= page_title %></h1>

You can then set conditions in the set_title method to set a different titles for different actions in the controller. It's nice to be able to see all the relevant page titles within your controller.

JasonOng
  • 3,268
  • 3
  • 21
  • 17
  • 2
    It's (unfortunately) common practice to use this way, but it's bad practice to put page titles in your controller. A page title is something more related to the view. If you were to offer an XML alternative view for instance, what relevance does a page title have? – Aupajo Oct 09 '08 at 10:40
  • Yeah I agree that it's not quite a good idea but however it's an alternative that might be useful in some cases where different page titles needs to be birthed from complex conditions? – JasonOng Oct 10 '08 at 01:31
  • 1
    Hmm, I still think it's something that belongs as a helper for the view. My rule is to look at the XML version and see if it needs that variable or not. If not, it's something that an individual view needs. Make your controller as light as possible. – Aupajo Oct 10 '08 at 06:53
2

approach for page titling using content_for method and a partial

1 . partial name : _page_title.html.erb

<%content_for :page_title do %>
 <%=title%>
<%end%>

2 . Usage in application.html.erb inside title tag

   <title><%=yield :page_title %></title>

3 . Usage in respective *.html.erb excluding application.html.erb Just stick this in any .html.erb and supply title of the page

    e.g : inside index.html.erb

    <%=render '_page_title', title: 'title_of_page'%>
Kofi Asare
  • 97
  • 1
  • 5
2

There's already several good answers but here's how I eventually solved this in Rails 7 using a combination of several answers. This solution let's me:

  • Provide the entire title for full control
    e.g. "My awesome custom title"
  • Provide just a page title and it automatically appends the site title
    e.g. "Items - My App"
  • Provide nothing and just the site title is displayed
    e.g. "My App"
  • Localise all of the above

Set up

Localisation

# config/locales/en.yml
en:
  layouts:
    application:
      site_title: "My App"

Logic in template

# app/views/layouts/application.html.erb
<title>
  <%= yield(:title).presence ||
      [yield(:page_title).presence, t(".site_title")].compact.join(" - ") %>
</title>

Logic in helper

Note the switch from yield to content_for

# app/views/layouts/application.html.erb
<title><%= show_title %></title>

# app/helpers/application_helper.rb
module ApplicationHelper
  def show_title
    content_for(:title).presence ||
      [content_for(:page_title).presence, t(".site_title")].compact.join(" - ")
  end
end

Usage

Fully custom title

# app/views/static_pages/welcome.html.erb
<% content_for :title, "My awesome custom title" %>

# or localised...
# <% content_for :title, t(".custom_title") %>

# Outputs:
# <title>My awesome custom title</title>

Page title (with site automatically appended)

# app/views/products/index.html.erb
<% content_for :page_title, "Items" %>

# or localised...
# <% content_for :page_title, t("Products.model_name.human") %>

# Outputs:
# <title>Items - My App</title>

Default page title

# No "content_for" specified

# Outputs:
# <title>My App</title>

Notes on the logic

  • .presence is needed to turn empty strings (like yield(:title) might be) into nils
  • If yield(:title).presence is nil, the || tells Ruby to evaluate the other side of the expression
  • On the other side, we start with an array of the page title and the site title
  • .compact removes nils so if the page title is blank we don't end up with " - My App"
  • `.join(" - ") joins the items separated by a hyphen if there's more than one, returns the only item unmodified if there's only one and returns an empty string if the array is empty
Matthew
  • 1,300
  • 12
  • 30
1

In short, I can write this down as follow

<%content_for :page_title do %><%= t :page_title, "Name of Your Page" %> <% end %>
Mukesh Kumar Gupta
  • 1,567
  • 20
  • 15
1

I like opsb's method, but this method works too.

<% provide(:title,"ttttttttttttttttttZ") %>
<html>
  <head><title><%= yield(:title) %></title></head>
   <body></body>
</html>
barlop
  • 12,887
  • 8
  • 80
  • 109
  • FWIW the method here is the method used in hartl's book chapter 3, not that I recommend that book as I find it a bit of a hassle to follow through but anyhow. – barlop Jan 14 '18 at 00:27
1

I wanted to ensure that translations was always provided for the current page.

This is what I came up with in app/views/layouts/application.html.erb:

    <title>
      <%= translate("#{controller_name}.#{action_name}.site_title", raise: true) %>
    </title>

This will always look for a translation for the current view and raise an error if none is found.

If you instead wanted a default you could do:

    <title>
      <%= translate("#{controller_name}.#{action_name}.site_title", 
          default: translate(".site_title")) %>
    </title>
Emric Månsson
  • 495
  • 6
  • 15
0

I use this plugin these Rails helpers I wrote: http://web.archive.org/web/20130117090719/http://henrik.nyh.se/2007/11/my-rails-title-helpers/

rakvium
  • 64
  • 1
  • 7
Henrik N
  • 15,786
  • 5
  • 82
  • 131
-1

The best/clean way to do this :

<title><%= @page_title or 'Page Title' %></title>
FouZ
  • 61
  • 7
-9

I would like to add my pretty simple variant.

In the ApplicationController define this method:

  def get_title
    @action_title_name || case controller_name
                            when 'djs'
                              'Djs'
                            when 'photos'
                              'Photos'
                            when 'events'
                              'Various events'
                            when 'static'
                              'Info'
                            when 'club'
                              'My club'
                            when 'news'
                              'News'
                            when 'welcome'
                              'Welcome!'
                            else
                              'Other'
                          end
  end

After that you can call get_title from your layout's title tag. You can define more specific title for your page by defining @action_title_name variable in your actions.

IDBD
  • 298
  • 2
  • 6
  • 2
    I guess you've learned the concept of [separation of concerns](http://en.wikipedia.org/wiki/Separation_of_concerns) during the past six years, but I will just put it out there for others who might be confused about why this answer got negative marks. – nurettin Oct 02 '14 at 10:33