Building flexible menu in rails

I am not using this technique anymore! If you're looking to build menus in rails, I suggest you read about my new technique. I am keeping this post public for academic reasons

If you've ever developed a dynamic website, chances are that you had to implement a dynamic menu or a breadcrumb. You know, something like this (taken from Ecrire's admin bar):

Menu used on this blog!

Here's how I did it, without gems.

Basic needs

When I browse for a gem to do that, I wasn't too sure about what I wanted. Many gems tried to be too smart about what they do. What if you want a dumb menu renderer, something you can feed it menu items and it renders them for you. Something that needs to:

  1. Add arbitrary menu item from the controller;
  2. Evaluate the request to see if the item should be highlighted;
  3. Change the CSS class/id like you would with a normal tag;
  4. Render the menu at the place you want.

In essence, this is how the menu could be used:

# app/controllers/application_controller.rb

class ApplicationController < ActionController::Base

helper_method :menu

def menu

@menu ||= Admin::MenuHelper::Menu.new request do |menu|

menu.add :blog do |item|

item.path = root_path

item.label = "Home"

item.id = 'blogPublicHome'

item.highlight_when do |request|




menu.add :posts do |item|

item.path = admin_posts_path

item.label = I18n.t('admin.navigation.posts')

item.highlight_when do |request|




menu.add :partials do |item|

item.path = admin_partials_path

item.label = I18n.t('admin.navigation.partials')

item.highlight_when do |request|






Start with ruby objects, end up with HTML

The idea with this menu is that you want to leverage plain old ruby objects (PORO) to store your links data until you are ready to render your menu.

So the goal is to have two classes: Menu and Item. From there, you want to be able to call Menu#render and have your menu rendered in HTML.

In this example, the ruby classes will live within a helper. Why in a helper you ask? Because it helps you render a menu on the screen, that's why.

I wanted to focus on readability and maintenance. Sure there's shorter way to do this but shorter doesn't always mean better. Anyhow, here's how it looks like.

# app/helpers/admin/menu_helper.rb

module Admin

module MenuHelper

class Menu

include ActionView::Helpers

include ActionView::Context

def initialize(request)

@request = request

@items = Hash.new.with_indifferent_access

raise StandardError unless block_given?

yield self


def add(name, klass = Admin::MenuHelper::Item)

raise ArgumentError unless block_given?

raise IndexError if @items.has_key?(name)

item = klass.new(@request)

@items[name] = item

yield item


def render




class Item

include ActionView::Helpers

include ActionView::Context

include Rails.application.routes.url_helpers

attr_reader :request, :css

attr_accessor :path, :label, :id

def initialize(request)

@request = request

@css = %w(link)


def highlight_when(&block)

raise ArgumentError if block.nil?

@callback = block


def render

raise StandardError, "Admin::MenuHelper::Item is not configured properly, it's missing a callback" if @callback.nil?

@css << "active" if @callback.call(@request)

link_to(@label, path, class: @css, id: @id)






Line #5-6: You need to include the ActionView helpers to have access to the methods within a view i.e. content_tag, link_to, etc.

Line #11-12: Raise an exception if no block is given. I did this because I wanted to make sure some items would exist from the get go. The block is then called and a reference to this menu is passed so it's possible to use menu.add.

Line #15: Where your add items to your menu. This method has a klass argument that is optional should I want to customize an item with a different class. A block must be given. More on this later.

Line #23: Renders the menu to the screen. What it does is that it passes the request object to each item so they can check if they have to be highlighted and then iterates every items and render them.


Line #29-30: Same as above, included to gain access to the view's helper methods.

Line #42: It's the block that will be called to check if the item should be highlighted or not.

Line #47: The render block that will generate the HTML output for this item. The item will have a class named active if the block needs to be highlighted.

The finale

Once you have all of your items configured, you can render it using menu#render

# app/views/layout/application.html.erb



<%= menu.render %>



I hope you like this!

Get more ideas like this to your inbox

You will never receive spam, ever.