Tutorial for Adding Tabs to Rails Using Tabulous

Step-by-Step Tutorial for Creating a Tabbed Rails Application

This tutorial shows you how to make a simple Rails application from scratch that has both tabs and subtabs.

Update: This blog post is about an old version of tabulous. Tabulous 2 has a completely different syntax. This tutorial should still work except for the parts referring to the app/tabulous/tabulous.rb file. Read more about the new version of tabulous.

Step 1: Scaffolding the Application

Let’s say you want to keep track of all the fonts you like and all of the design-related websites you think are cool. And let’s also say that you don’t believe in using a simple text file but feel compelled to build an entire Rails app just for this purpose. (Who said this wasn’t going to be contrived?)

First, start by creating a Rails 3 app:

$ rails new contrived

And setting up its gems:

$ cd contrived
$ bundle install

And let’s add some scaffolding to keep track of our websites, serif fonts and sans serif fonts:

$ rails generate scaffold Website url:string
$ rails generate scaffold SerifFont name:string
$ rails generate scaffold SansSerifFont name:string

Now let’s hook up the root URL to point to the list of websites by adding this line to config/routes.rb:

  root :to => 'websites#index'

And remove the autogenerated index.html file:

$ rm public/index.html

Set up the database:

$ rake db:create
$ rake db:migrate

Now we are all set to fire up the app!

$ rails server

Navigating to http://localhost:3000 should show you something like this:

Step 2: Adding Tabs

Let’s use the ultrafantastically superb tabulous gem to add tabs to this app. Add the following to your app’s Gemfile:

gem 'tabulous'

And then

$ bundle install

Now type the following magical line:

$ rails generate tabs

which will create a file called app/tabs/tabulous.rb which comes included with wonderful explanatory comments, free of charge. Here’s what the file looks like without comments:

Tabulous.setup do |config|
 
  config.tabs do
    [
      #------------------------------------------------------------------------------------------------------------------------#
      #    TAB NAME                 |    DISPLAY TEXT          |    PATH                     |    VISIBLE?    |    ENABLED?    #
      #------------------------------------------------------------------------------------------------------------------------#
      [    :websites_tab            ,    'Websites'            ,    root_path                ,    true        ,    true        ],
      [    :websites_tab            ,    'Websites'            ,    websites_path            ,    true        ,    true        ],
      [    :serif_fonts_tab         ,    'Serif Fonts'         ,    serif_fonts_path         ,    true        ,    true        ],
      [    :sans_serif_fonts_tab    ,    'Sans Serif Fonts'    ,    sans_serif_fonts_path    ,    true        ,    true        ],
      #------------------------------------------------------------------------------------------------------------------------#
      #    TAB NAME                 |    DISPLAY TEXT          |    PATH                     |    VISIBLE?    |    ENABLED?    #
      #------------------------------------------------------------------------------------------------------------------------#
    ]
  end
 
  config.actions do
    [
      #----------------------------------------------------------------------------#
      #    CONTROLLER           |    ACTION          |    TAB                      #
      #----------------------------------------------------------------------------#
      [    :websites            ,    :all_actions    ,    :websites_tab            ],
      [    :websites            ,    :all_actions    ,    :websites_tab            ],
      [    :serif_fonts         ,    :all_actions    ,    :serif_fonts_tab         ],
      [    :sans_serif_fonts    ,    :all_actions    ,    :sans_serif_fonts_tab    ],
      #----------------------------------------------------------------------------#
      #    CONTROLLER           |    ACTION          |    TAB                      #
      #----------------------------------------------------------------------------#
    ]
  end
 
  config.active_tab_clickable = false
  config.always_render_subtabs = false
  config.raise_error_if_no_tab_found = true
  config.html5 = false
  config.css.scaffolding = true
  # config.css.background_color = '#ccc'
  # config.css.text_color = '#444'
  # config.css.active_tab_color = 'white'
  # config.css.hover_tab_color = '#ddd'
  # config.css.inactive_tab_color = '#aaa'
  # config.css.inactive_text_color = '#888'
 
end

You’ll notice that the file already has some tab data setup. Tabulous inspects your routes and tries to guess what tabs you may want, microsoft-clippy-style. Which means it gets it wrong. We really don’t need two websites tabs, so let’s delete one of them:

  config.tabs do
    [
      #------------------------------------------------------------------------------------------------------------------------#
      #    TAB NAME                 |    DISPLAY TEXT          |    PATH                     |    VISIBLE?    |    ENABLED?    #
      #------------------------------------------------------------------------------------------------------------------------#
      [    :websites_tab            ,    'Websites'            ,    websites_path            ,    true        ,    true        ],
      [    :serif_fonts_tab         ,    'Serif Fonts'         ,    serif_fonts_path         ,    true        ,    true        ],
      [    :sans_serif_fonts_tab    ,    'Sans Serif Fonts'    ,    sans_serif_fonts_path    ,    true        ,    true        ],
      #------------------------------------------------------------------------------------------------------------------------#
      #    TAB NAME                 |    DISPLAY TEXT          |    PATH                     |    VISIBLE?    |    ENABLED?    #
      #------------------------------------------------------------------------------------------------------------------------#
    ]
  end
 
  config.actions do
    [
      #----------------------------------------------------------------------------#
      #    CONTROLLER           |    ACTION          |    TAB                      #
      #----------------------------------------------------------------------------#
      [    :websites            ,    :all_actions    ,    :websites_tab            ],
      [    :serif_fonts         ,    :all_actions    ,    :serif_fonts_tab         ],
      [    :sans_serif_fonts    ,    :all_actions    ,    :sans_serif_fonts_tab    ],
      #----------------------------------------------------------------------------#
      #    CONTROLLER           |    ACTION          |    TAB                      #
      #----------------------------------------------------------------------------#
    ]
  end

Let’s look at these two tables in more detail. The first table is where you define your tabs. The order that you define them in is the order that they appear in the view. The columns contain Ruby expressions that are evaluated in the context of a Ruby view (which is why we can use route helpers). The second table is necessary so that when a controller action is called we know what tab should be selected. :all_actions is a shortcut; you can explicitly list actions out, one on each row.

The next thing we need to do is tell tabulous where in the view the tabs should be rendered. Tabulous gives us two view helpers: tabs and subtabs. Open up your app/views/layouts/application.html.erb file and add them like so:

<!DOCTYPE html>
<html>
<head>
  <title>Contrived</title>
  <%= stylesheet_link_tag :all %>
  <%= javascript_include_tag :defaults %>
  <%= csrf_meta_tag %>
</head>
<body>
 
<%= tabs %>
<%= subtabs %>
<%= yield %>
 
</body>
</html>

Restart your Rails server and reload http://localhost:3000 in your browser. You should see something like this:

And there you have it. Tabs in Rails! That wasn’t so hard, was it?

Notice that the tabs are styled for you. Tabulous adds CSS scaffolding to get you started but you should eventually turn off the scaffolding and use your own CSS. If you view the HTML source you can see the CSS that tabulous generated which can give you a head start.

Step 3: Adding Subtabs

What you really want is to have a fonts tab with two subtabs: serif and sans serif. To do that, change your app/tabs/tabulous.rb file to the following:

  config.tabs do
    [
      #---------------------------------------------------------------------------------------------------------------------#
      #    TAB NAME                    |    DISPLAY TEXT    |    PATH                     |    VISIBLE?    |    ENABLED?    #
      #---------------------------------------------------------------------------------------------------------------------#
      [    :websites_tab               ,    'Websites'      ,    websites_path            ,    true        ,    true        ],
      [    :fonts_tab                  ,    'Fonts'         ,    serif_fonts_path         ,    true        ,    true        ],
      [    :serif_fonts_subtab         ,    'Serif'         ,    serif_fonts_path         ,    true        ,    true        ],
      [    :sans_serif_fonts_subtab    ,    'Sans Serif'    ,    sans_serif_fonts_path    ,    true        ,    true        ],
      #---------------------------------------------------------------------------------------------------------------------#
      #    TAB NAME                    |    DISPLAY TEXT    |    PATH                     |    VISIBLE?    |    ENABLED?    #
      #---------------------------------------------------------------------------------------------------------------------#
    ]
  end
 
  config.actions do
    [
      #-------------------------------------------------------------------------------#
      #    CONTROLLER           |    ACTION          |    TAB                         #
      #-------------------------------------------------------------------------------#
      [    :websites            ,    :all_actions    ,    :websites_tab               ],
      [    :serif_fonts         ,    :all_actions    ,    :serif_fonts_subtab         ],
      [    :sans_serif_fonts    ,    :all_actions    ,    :sans_serif_fonts_subtab    ],
      #-------------------------------------------------------------------------------#
      #    CONTROLLER           |    ACTION          |    TAB                         #
      #-------------------------------------------------------------------------------#
    ]
  end

Notice how subtabs’ names must end in _subtab. Also notice how the subtabs immediately follow the tab that groups them. Finally, notice in the actions section how we hooked up the controllers to the subtabs. For example, when an action of the SerifFontsController is rendered, the :serif_fonts_subtab will be selected. Since tabulous knows that the :fonts_tab is its parent, the :fonts_tab will also appear selected.

After you edit the tabulous.rb file you may notice the tables are all out of alignment. To prettify the file:

$ rake tabs:format

Refresh your browser. When you click on the “Fonts” tab you should now see subtabs like this:

The markup that’s generated looks like this:

<div id="tabs">
  <ul>
    <li class="inactive enabled"><a href="/websites" class="tab">Websites</a></li>
    <li class="active enabled"><span class="tab">Fonts</span></li>
  </ul>
</div>
<div id="subtabs">
  <ul>
    <li class="active enabled"><span class="tab">Serif</span></li>
    <li class="inactive enabled"><a href="/sans_serif_fonts" class="tab">Sans Serif</a></li>
  </ul>
</div>

Step 4: Profit!

There are many other things that tabulous can do, such as support HTML5 and disabled tabs. The best place to learn about the full capabilities of tabulous is to read the comments in the app/tabs/tabulous.rb file after you generate it.

You can also watch my live demo of tabulous that I presented to the Boston Ruby Group. They asked some really good questions.

Happy tabbing!

You can skip to the end and leave a response. Pinging is currently not allowed.

51 Responses to “Tutorial for Adding Tabs to Rails Using Tabulous”

  1. Thanks, can you cache the actions of the gem in production mode?

  2. techiferous says:

    @stephen I’m not sure what you are asking for. The app/tabs/tabulous.rb file is reloaded on each request in development mode but only loaded once in production. Is that what you mean?

  3. Niklas says:

    Does taboulous has I18n support? Can I translate the names on the tabs into the current locale?

  4. techiferous says:

    @Niklas Yes. The Ruby code that you put in tabulous’s configuration table is rendered in the context of a Rails view, so you should have access to the t view helper in Rails. For example:

      config.tabs do
        [
          #---------------------------------------------------------------------------------------------------------------------#
          #    TAB NAME                    |    DISPLAY TEXT    |    PATH                     |    VISIBLE?    |    ENABLED?    #
          #---------------------------------------------------------------------------------------------------------------------#
          [    :websites_tab               ,    t(:websites)    ,    websites_path            ,    true        ,    true        ],
          [    :fonts_tab                  ,    t(:fonts)       ,    serif_fonts_path         ,    true        ,    true        ],
          [    :serif_fonts_subtab         ,    t(:serif)       ,    serif_fonts_path         ,    true        ,    true        ],
          [    :sans_serif_fonts_subtab    ,    t(:sans_serif)  ,    sans_serif_fonts_path    ,    true        ,    true        ],
          #---------------------------------------------------------------------------------------------------------------------#
          #    TAB NAME                    |    DISPLAY TEXT    |    PATH                     |    VISIBLE?    |    ENABLED?    #
          #---------------------------------------------------------------------------------------------------------------------#
        ]
      end
  5. ElliotB says:

    How is tabulous different from tabsonrails? It seems both do similar things. Anyone have experience working with both? Advantages vs disadvantages of both?

  6. techiferous says:

    @ElliotB tabulous and TabsOnRails solve a similar problem but in a different way. So it’s mostly a matter of which syntax you prefer.

  7. mikeK says:

    Awesome tutorial! Makes it easy to pick up. Thanks for the effort. I am trying to use tabulous for tabs and subtabs but am having problems getting it to work.

    In my app the user needs to fill out multiple forms related to a property. I want to use sub tabs to move between the various forms. So the main tab is called ‘Property’ and the sub tabs would be ‘General’, ‘Finances’, etc.

    I am using nested resources like this:

      resources :properties do
        resources :general, :only => [:index], :controller => 'properties/general'
        resources :finances, :only => [:index], :controller => 'properties/finances'
        resources :income, :only => [:index], :controller => 'properties/income'
        resources :expenses, :only => [:index], :controller => 'properties/expenses'
        resources :projections, :only => [:index], :controller => 'properties/projections'
        resources :charts, :only => [:index], :controller => 'properties/charts'
        resources :proposals, :only => [:index], :controller => 'properties/proposals'
      end

    I get No route matches {:controller=>”properties/general”} when the id is nil when I am creating a property. The problem appears to be when I want to create a new property and the property id does not exist yet. Do I need to add extra code around something? The path value in tabulous.rb is property_general_index_path(params[:id]). If use a hardcoded value instead of ‘params[:id]’ this error goes away.

    Hope I explained this well enough.

  8. techiferous says:

    @mikeK I’m glad you’re finding tabulous useful!

    The important thing to keep in mind is that all the paths in tabulous.rb are evaluated when a view calls < %= tabs %> or < %= subtabs %>. So if the paths defined in tabulous.rb are expecting params[:id] to have a value, they will break in the views where params[:id] is nil. You can add a Ruby conditional inside of tabulous.rb so that when params[:id] is nil it returns a different path. Don’t forget that you can also conditionally disable tabs in case some of the subtabs shouldn’t be clicked until a property is defined.

    If this advice doesn’t help, feel free to post your tabulous.rb file and I’ll try to help you again.

  9. Jason says:

    First, thank you for sharing. I’m liking what I’ve seen so far, but I can not figure out how to get multiple tab sets. For instance, my public site will have tabs and my admin site will have a separate set of tabs. Any recommendations how I can accomplish this?

  10. techiferous says:

    @Jason One of the design assumptions is that there is only one set of tabs. However, having more than one set of tabs seems to be a common enough use case that I would consider adding the capability in a future version of tabulous.

    If both sets of tabs are never visible at the same time, one way you can get around this limitation is to hide all of the admin tabs on the public site and vice versa. Otherwise, you may have to leave tabulous behind and code the tabs yourself. Or you can try out Tabs on Rails which supports multiple tab sets.

  11. Rick says:

    This looks like a great tool! Any chance you could include how to set it up using Postgres instead of SQLite? TIA….

  12. techiferous says:

    @Rick This is database agnostic. It only deals with the view and controller layer, not the model layer. SQLite is used for the test applications only, so you don’t need it to use tabulous.

  13. Derek says:

    Great gem, Wyatt. I definitely prefer this to “tabs on rails.” However, I’m having trouble figuring out your logic of NOT rendering the tabs when on a page that is NOT a tab.

    So, for example, I’m using tabulous for my main navigation, but I also have certain tasks like “Account Settings” that aren’t a tab (or even a sub tab). Thus, when editing my account settings, the entire nav disappears!

    Any way around this?

  14. techiferous says:

    @Derek Thanks. Tabs on Rails looks pretty cool, but I’m a big fan of having choices and it’s nice that some people are choosing tabulous. :)

    I believe that for best usability, when tabs are displayed, there should always be a selected tab. Tabs assist the user in navigation. Tabs tell the user both where they are and how they got there. If you have a view showing, like your “Account Settings” view, and the tabs are also showing but none of them are selected, the user doesn’t have that nice indication of where they are.

    Another reason is that some views, like the login view, are usually not rendered with tabs. So when we do not hook up the login action to a tab, tabulous assumes we have a view that does not want tabs displayed.

    So I would suggest your “Account Settings” view should be a tab or subtab. If not, clicking on the “Account Settings” link should probably open a popup or lightbox.

    It’s possible I’m misunderstanding your situation and that you are using your “Account Settings” and other links as a separate set of tabs. Currently, tabulous only supports one set of tabs, but if I see enough demand I would consider adding support for multiple tab sets.

  15. Chetstone says:

    Is there a way to put code in the action table?
    I’d like to use subtabs in one of my views to select sorting options. So the subtabs would all have the same controller and action, just different params. How can I active the current subtab in such a case?

    thank you for tabulous — pretty straightforward to use. And don’t worry about the rake task. Which of us does not use our text editor to help with indentation? So until somebody creates a tabulous-mode in emacs, the rake task is handy.

  16. techiferous says:

    @Chetstone Unfortunately, I did not anticipate that use case. One way to work around this is to create your own subtabs view helper and call it instead of tabulous’s when you are rendering those sorting views. I’ll consider adding support for this use case in a future version of tabulous.

    You can add code to the action table, but the code still needs to evaluate to a symbol that represents a controller, action, and tab. So that won’t help you since you need to work with the parameters.

  17. Chen says:

    Hi,

    Is there any way to make a vertical tabs and dynamic menu using tabulous?

  18. techiferous says:

    Hi Chen,

    If you turn off the CSS scaffolding, you can use your own CSS which allows you to style the tabs vertically. However, the subtabs are not dynamically generated so you would have to create a dynamic menu with some other tool.

  19. Yasar says:

    Hi,

    I do this all steps but the tabs are not show in the browser, I don’t know what is the problem. can anybody help this problem.

    Regards,
    Yasar

  20. techiferous says:

    Do you have the following in a view?

    <%= tabs %>
    <%= subtabs %>

    Also make sure you have the =. Sometimes I accidentally type <% instead of <%= and nothing shows up.

  21. dgfrancisco says:

    Hi @techiferous its a nice gem really helpful, I have a qs, can I do drop menus with this gem? cuz I see the options for submenus but the user only will see the subtabs when click not when hover the tab.
    So, I missed something? thanks

  22. techiferous says:

    @dgfrancisco: No, that’s not possible with this version of tabulous (sorry) but I’d like to add that to a future version.

  23. Ramprasad says:

    Hi techiferous,
    I am building an application that allows users to login and display some contents using tabs. I display options such as logout at the top and then i use to display the tabs below the options. Tabulous was exactly what I was looking for. But there seems to be a problem like displays the contents that appear inside selected tab only when that is included in views/layouts/application.html.erb. If I include that inside model’s view (in my case inside views/users/tabsframe.html.erb), on clicking a tab (say “closed” tab) control moves to that page (0.0.0.0:3000/closed) rather than displaying the contents of “closed” inside the tab within tabsframe. All these views belong to the same model. Also I am not able to highlight the selected tab. Please let me know what modifications I need to make or if I am not clear with my explanation of the problem. Thanks.

  24. techiferous says:

    Hi Ramprasad,

    Tabulous is “opinionated” and is designed to approach tabs in a certain way. You may be using tabulous in a way in which it was not intended. But you may have uncovered a use case that I didn’t anticipate and want to support. It’s unclear to me; I can’t tell what’s going on based on how you described your scenario. If you provide some more detail I’ll be glad to help you out. For example, are you calling < %= tabs %> in a view instead of a layout? Keep in mind that tabulous is not designed to work with Ajax or JavaScript.

  25. Ramprasad says:

    Hi,

    Yeah. You are right. I am calling in a view (say view1) and not from layout. What I need is, on clicking the tab I need to display another view (say view2). How would I define the path for view2 in routes.rb such that I can use that in tabulous.rb? Thanks.

Leave a Reply