Read my latest article: Planet Argon Blog (posted Wed, 17 Feb 2010 15:11:00 GMT)

Managing SEO-friendly HTML Titles with Rails

Posted by Robby Russell Wed, 26 Mar 2008 21:41:00 GMT

28 comments Latest by Telecommunications Thu, 20 Aug 2009 08:27:47 GMT

I’ve seen this come up a few times in the #rubyonrails IRC channel and figured that I’d post a quick entry for future reference.

Problem: HTML titles

You want to have a clean way to manage the titles on your HTML pages.


  <html>
    <head>
      <title>Robby on Rails &mdash; Article Title Goes Here</title>
    </head>
    <body>
      ...

Possible Solution(s):

Since the <title> tag is usually declared in your layout, you need to be able to dynamically update this information from almost every action in your application.

Here are a few ways that I’ve seen this handled.

  1. Use a instance variable, which would have a default value and you could override it in any controller action
  2. Use the content_for method to manage it.

Let’s take a few minutes to look at these two approaches.

Instance Variable

With the instance variable, you might end up with something like:


  # app/views/layouts/application.html.erb
  <title>Robby on Rails &mdash; <%= @html_title || 'Default text here...' -%></title>

Then in a controller action…


  # app/controllers/articles_controller.rb
  def show
    # ...
    @html_title = @article.title
  end

So, that’s one way to handle it and is probably a more common way.

The content_for helper method approach

This solution is very similar (and underneath uses an instance variable).

We’ll use the content_for and a little yield action.


  # app/views/layouts/application.html.erb
  <title>Robby on Rails <%= (html_title = yield :html_title) ? html_title : '&mdash; Default text here...' %></title>

Then we’ll create a helper method.


  # app/helpers/application_helper.rb
  def set_html_title(str="")
    unless str.blank?
      content_for :html_title do
       "&mdash; #{str} " 
      end
    end
  end  

Now, instead of defining the HTML <title> value in the controllers, we’ll just toss this into our html.erb files as necessary.


  <% set_html_title(@article.name) -%>
  ... rest of view

..and that’s pretty much it.

Which is the better solution?

This is where we’ll not find a lot of consensus amongst people. I’m a fan of the content_for-based approach and defining the title in views rather than in controller actions. I’m an advocate of skinny controllers and while I’m not a big fan of messy views, I believe that there is less overhead in managing this within the View-world.

I’d love to hear your thoughts on this. Perhaps you have a more eloquent for managing things like this? Do share. :-)

Subscribe to my RSS feed Enjoying the content? Be sure to subscribe to my RSS feed.
Comments

Leave a response

  1. Avatar
    Jörg Battermann Wed, 26 Mar 2008 22:26:19 GMT

    Well the way I do it, is basically set a variable in the application.rb controller and print that out in the layout:

    in application.rb:

    before_filter :meta_defaults

    def meta_defaults
      @meta_title = "Welcome to" 
      @meta_keywords = "a,b,c" 
      @meta_description = "Some Description" 
    end

    the application.html.erb looks like thise wise:

    <title><%= @meta_title %> my site</title>

    ... now if I want to have a different title there, I simply overwrite the @meta_title in my controller’s action.

    Not sure it is entirely clean/dry etc.. but works perfectly fine for me :)

    -J

  2. Avatar
    Timur Vafin Wed, 26 Mar 2008 23:19:27 GMT

    By the way, just put <% @title = ‘My mega title’ %> inside view, huh?

  3. Avatar
    Robby Russell Wed, 26 Mar 2008 23:27:16 GMT Recommend me on Working with Rails

    @Timur: yeah, you could do that too. Although, I’m not a fan of setting instance variables in my views. It feels cleaner (to me)... to call a helper in the view to define it than define a variable value (even if that’s what happening in the background).

  4. Avatar
    Jack Danger Canty Wed, 26 Mar 2008 23:28:07 GMT

    Since the action views are generated before the layout you can set the instance var there instead of setting it in the controller.

    I’ve never found a great solution to this primarily because I’m not sure what design pattern I’m looking for with my title content anyway. The pattern is clear for blogs or forums but not so clear for more unique apps.

    Oh, and one reason to define it in the views rather than controllers is because sometimes (like with an invalid record) the ‘create’ action displays the ‘new’ view. So you want what actually shows up to define the title.

  5. Avatar
    Robby Russell Wed, 26 Mar 2008 23:33:02 GMT Recommend me on Working with Rails

    Another reason that I prefer the helper approach is that you can have this be where you define your delimeter. In my example above, I used a mdash.

    If you want to have a few levels down you can call the helper a few times.

    For example… to produce:

    Planet Argon – Services – Rails Consulting

    I might do the following:

    <% set_html_title('Services') %>

    <% set_html_title('Rails Consulting') %>

    I suppose the helper could modified you to provide it an array of strings and call it like:

    <% set_html_title( 'Services', 'Rails Consulting' ) %>

    to produce the desired output.

  6. Avatar
    Andrew Nesbitt Wed, 26 Mar 2008 23:50:15 GMT

    I usually it in at the top of my views with:

    <% @title = "Editing #{@document.name}" %>

    and then in the application layout do something like this:

    <title>Governoration.com - <%= gently_escape(@page_title) || "A simple, powerful online management tool for school governing bodies" %></title>

    where gently_escape makes sure the result is valid xhtml and still pretty, the or means there is always a useful title for seo.

    I also do the same with some other meta tags like author, description and keywords with instance variables if the content is available on that page.

    <meta name="description" content="<%= gently_escape(@meta_description) || "Governoration is an online communication and resource facility for school Governing Bodies. By providing instant access to policies, minutes, reports and other related documents Governors can make decisions and remain informed" %>" />
  7. Avatar
    mikong Thu, 27 Mar 2008 00:55:57 GMT

    An old episode (30) of Ryan Bates’ Railscasts discussed this and suggests the content_for helper. I guess the only difference is he wanted to make it so clean that he made the name of the helper method simply title.

    <% title 'My Title' %>
  8. Avatar
    Marcus Derencius Thu, 27 Mar 2008 01:35:58 GMT
  9. Avatar
    Marcus Derencius Thu, 27 Mar 2008 01:36:19 GMT
  10. Avatar
    Winfield Thu, 27 Mar 2008 02:05:33 GMT

    I would argue that the page title is entirely a view responsibility and as such has no place being set within the controller.

    Secondly, the usage of a helper to provide the function of injecting the page title seems overly complex.

    In our organization we inject the title via content_for, using the one-line version (since it’s a short string):

    <% content_for :title, ‘Hello World’ %>

    Simple, with all responsibility for over-riding the title in the individual view page.

  11. Avatar
    Winfield Thu, 27 Mar 2008 02:08:26 GMT

    Many developers seem shocked to see the one line content_for form, since the block form is much more common.

  12. Avatar
    Tim Lucas Thu, 27 Mar 2008 04:06:30 GMT

    For SEO purposes I’d much prefer simply “Managed SEO-friendly HTML Titles with Rails” than having a prefix as you’re not giving up density, and really how much use is the site title? So if we throw away the need for prefixes and delimiters you pretty much don’t need a helper either.

    The second issue is HTML escaping. You may want to include a html entity in the title, so you can’t simply escape it in the layout, but you need to ensure user generated content is escaped. At the point of the application layout I assume all content is suitably escaped.

    So these days I stick with the following convention to begin with:

    <%
      @title = "Project #{h @project.title} Images" 
      @section, @subsection = :project, :images
    -%>

    In the layout I simply force everyone to explicitly set page titles (if only there was a way to enforce the quality of the title too)

    <%= @title || raise("No page title specified") %>

    My only suggestion, and maybe its stating the obvious, is not to cargo cult someone else’s approach and do whatever makes sense in terms of your immediate needs and your own conventions.

  13. Avatar
    bryan liles Thu, 27 Mar 2008 12:01:08 GMT

    I came up with something that was awfully similar to the RailsCast approach. I even made a helper method called title. This is what I’m going to stick with.

  14. Avatar
    James Chan Thu, 27 Mar 2008 12:54:13 GMT

    I prefer the content_for way, but for most of projects I use the headliner plugin.

  15. Avatar
    simon Thu, 27 Mar 2008 14:09:11 GMT

    i actually use the instance variable method on most projects, but recently have found that the content_for method is much better – for one simple reason:

    when you have a create action and you use the instance variable method you have to set it before doing render :action => ‘new’ if validation of the record fails

  16. Avatar
    Mike Nicholaides Fri, 28 Mar 2008 19:19:49 GMT
  17. Avatar
    Mike Nicholaides Fri, 28 Mar 2008 19:20:03 GMT
  18. Avatar
    jerome Tue, 01 Apr 2008 16:58:10 GMT

    winfield: +1 . Respect the MVC paradigm: the title should be defined in the view

    badrobby: I do have the same helper, althought the method name is title instead of set_html_title, then it’s much more seksy combined with haml:

    - title @article.title
    
  19. Avatar
    jerome Tue, 01 Apr 2008 16:58:22 GMT

    winfield: +1 . Respect the MVC paradigm: the title should be defined in the view

    badrobby: I do have the same helper, althought the method name is title instead of set_html_title, then it’s much more seksy combined with haml:

    - title @article.title
    
  20. Avatar
    jerome Tue, 01 Apr 2008 16:58:23 GMT

    winfield: +1 . Respect the MVC paradigm: the title should be defined in the view

    badrobby: I do have the same helper, althought the method name is title instead of set_html_title, then it’s much more seksy combined with haml:

    - title @article.title
    
  21. Avatar
    Graham Sun, 06 Apr 2008 22:12:47 GMT

    jerome – the title is data. Just because you display it in the view it doesn’t mean you need to define it there.

    Titles are often dynamically generated, and views can be quite reasonably shared between controllers. A helper method that returns the title seems to be a flexible approach to me; if you have site wide logic for generating your title you can define it in ApplicationHelper and override the method in other helpers as required. In order to keep things DRY I’ve even set up content_for hooks that can be used to override the title set by the helper, but I only use content_for when I need to make an exception to the rule. Admittedly this complexity comes in more handy when dealing with h1 tags than it does the title tag, as you may want to put HTML tags within your h1 tag.

    From an SEO perspective I’m reliably informed that only the first seven words of the title are of any use as the engines tend to ignore the rest. Try and keep the relevant part of your title at the beginning. This also aids usability for people using systems that show the beginning of the title when windows are minimised in the status bar (i.e. they can tell which page is which even when the browser is minimised).

  22. Avatar
    g.zhen.ning Mon, 07 Apr 2008 11:53:01 GMT

    I prefer the content_for way.

  23. Avatar
    EP Tue, 08 Jul 2008 07:06:45 GMT

    There are good reasons to use both ways depending on the app. Titles may simply be a field value (like a post title) or may be much more complex. A view should never have to understand why (Controller) it is rendering something only what (Model) it is rendering and how (View).

    As soon as your views start having to understand what prompted the rendering you are out-of-bounds and creating a complex chain of dependencies. For example, you may render the page title in a number of languages depending on a user preference which is fine if there is a user logged in or if there is a translation available in the preferred language. Now the view is having to know a lot more about the workings of your application than it should and those decisions should be handled by the Controller.

  24. Avatar
    Henrik N Mon, 15 Dec 2008 17:00:34 GMT
  25. Avatar
    Antho Wed, 18 Feb 2009 09:02:50 GMT

    Just released a first version of the EasyTitles plugin

  26. Avatar
    www.avirtualhorse.com Fri, 20 Mar 2009 17:34:45 GMT

    There is one advantage content_for approach seems to have, you can use it with fragment caching. If you have a forum and you want to generate keywords, title, description based on the actual article/post, you’d most likely want to generate them based on your @article

    <% content_for :meta do %> <% cache ‘meta_#{params[:id]}’ do @article = Article.find_by_id(params[:id]) %>

    <%= @article.title %> = @article.content.slice(1,126)>”/>

    <% end %> <% end %>

  27. Avatar
    www.avirtualhorse.com Fri, 20 Mar 2009 17:38:44 GMT

    oh well, the code got screwed up.

    title : <%= @article.title %> description : <%= @article.content.slice(1,126) %>

    @article is fragment cached so this way for pages that don;t really change in time, it’s an opportunity to be good with your db server

  28. Avatar
    Telecommunications Thu, 20 Aug 2009 08:27:47 GMT

    Thanks for the knowledge on…....

Share your thoughts... (really...I want to hear them)

Comments