Signposts pointing in opposite directions ← Blog

Smart page titles in Ruby on Rails

Having separate titles for each page in a Rails application is a very common requirement and yet RoR does not provide any built in ways to deal with it. I am going to present you with some handy code that can solve this problem without the use of gems.

There are going to be 3 ways to define a page title:

  • For the whole controller.
  • For a specific action inside a controller.
  • Implicitly from a controller name.

Last is gonna be especially useful, you can just drop the following code into your application and have reasonably good titles.

So let’s add code to our parent ApplicationController:

class ApplicationController < ActionController::Base
  before_filter :set_page_title

  CONTROLLER_TITLES = Hash.new

  # for setting page titles for the whole controller
  def self.page_title title
    CONTROLLER_TITLES[self.name] = title
  end

  # for setting page titles for a specific actions.
  # this overrides @page_title previously set in before_filter
  def page_title title
    @page_title = title
  end

  private
  def set_page_title
    @page_title = CONTROLLER_TITLES[self.class.name] || controller_implicit_title
  end

  def controller_implicit_title
    @@controller_implicit_titles ||= Hash.new
    # remove any namespaces and mentions of controller
    # so Client::OrdersController becomes "Orders"
    @@controller_implicit_titles[self.class.name] ||= self.class.name.gsub(/(.+::)/, '').gsub('Controller', '').underscore.gsub('_', ' ').titleize
  end
end

And reference page title in our layout:

<!DOCTYPE html>
<html>
<head>
<title><%= @page_title || 'My Awesome App'%></title>
</head>
<body>
  <%= yield %>
</body>

After that setting page titles is extremely easy. You don’t even have to do it if you are naming your controllers properly.

class OrdersController < ApplicationController
  page_title 'Orders' # unless specified otherwise all pages are gonna have title 'Orders'

  def index # Page title == 'Orders'
    @orders = Order.all
  end

  def cancelled # Page title == 'Cancelled Orders'
    page_title 'Cancelled Orders'
    @orders = Order.cancelled
  end
end
class OrderNotesController < ApplicationController
  def index # Page title == 'Order Notes'
    @note = OrderNote.all
  end

  def order # Page title == 'Notes for Order #12345'
    page_title "Notes for Order ##{params[:order_id]}"
    @note = OrderNote.where(order_id: params[:order_id])
  end
end