Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
RAILS 3: nav_link helper for adding 'selected' class to navigation elements

UPDATE: It's a gem!

This helper has finally been moved into a gem called nav_lynx!

Thanks to @brianjlandau and @reagent for getting that set up and tested!

Behold, the nav_link:

The nav_link helper works just like the standard Rails link_to helper, but adds a 'selected' class to your link (or its wrapper) if certain criteria are met. By default, if the link's destination url is the same url as the url of the current page, a default class of 'selected' is added to the link.

<%= nav_link 'My Page', my_path %>

When my_path is the same as the current page url, this outputs:

<a class="selected" href="">My Page</a>

For more options and full usage details, see:

Drop nav_link_helper.rb into app/helpers in your Rails 3.x app and enjoy.

UPDATE: Now with block support!

Same usage as link_to:

<%= nav_link '' do %>
  <strong>My Page</strong>
<% end %>

jem commented Aug 8, 2012

This obviously goes beyond it, but might be good to mention the simple link_to_unless_current method available from ActionView as well.


greypants commented Aug 8, 2012

Yeah, that was actually where I started when first looking into this issue, and is definitely good to note. The main problem with that is just doesn't output the <a> tag by default, but just the text inside it if the page is 'selected'. That tends to visually break things when css styling relies on that element's presence. You can specify specific markup to output instead, but it gets a little messy.

murdoch commented Aug 10, 2012

I learned a lot from reading through the code here, thanks for sharing, I imagine I will use it often.

nice code ...
Will it be possible to place an additional and/or Tag inside the Tag?


  • Home
  • or something like
  • More
  • nice code ...
    Will it be possible to place an additional span-tag and/or b-tag inside the Link-Tag?

    li --> a --> span --> b

    span und b-Tag können auch classes haben

    I'm getting errors if I use the controller_segment option and the current url does not have the segment being looked for.

    It would be cool if this can accept a block as the last argument, just as link_to

    Has anyone successfully used this with dropdowns? Twitter bootstrap styled dropdowns are lists nested inside the wrapper

  • tag, which is what needs the "active" class assigned to it.

  • Owner

    greypants commented Jan 18, 2013

    Just updated to accept blocks!

    awakia commented Feb 4, 2013

    Really nice helpers and I used this a lot. Though it cause ActionController::RoutingError if the request method is other than GET. Just updating two methods fix this issue, so please update this gist. Thanks.

          def current_controller
    -      controller_for(@request.path)
    +      controller_for(@request.path, @request.request_method)
    -    def controller_for(path)
    -      Rails.application.routes.recognize_path(path)[:controller]
    +    def controller_for(path, method = "GET")
    +      Rails.application.routes.recognize_path(path, method: method)[:controller]

    leisti commented Feb 6, 2013

    This is useful. I have a couple of suggestions, though:

    First, please copy the usage instructions from to README.markdown. You
    never know when and if the article will become unavailable for some reason, and
    it's a good idea to keep everything to do with one gist in one place.

    Second, it would be nice if one could also give an option for defining a class
    name to be given for unselected links, in case the user wants to define
    properties for such links in a .css file.

    Third, I didn't really see the point of methods that are called from only one
    place in the code -- I think they can confuse rather than clarifying the code,
    so I merged the code of methods link_classes, html_options and link to method

    I also made a couple of changes to parameter and variable names, for better
    self-documentation and more clarity. These are the resulting changes to the
    code (tested to work):

      def to_html
    -   html = link
    -   if @options[:wrapper]
    -      html = content_tag(@options[:wrapper], html, :class => wrapper_classes)
    -   end
    -   html.html_safe
    +   linked_classes = nil
    +   if @html_options[:class]
    +     linked_classes = @html_options[:class] + " #{class_name_to_be_used}"
    +   elsif !@options[:wrapper_class]
    +     linked_classes = class_name_to_be_used
    +   end
    +   merged_html_options = @html_options.merge(class: linked_classes)
    +   the_link = link_to(@title, @path, merged_html_options)
    +   if @options[:wrapper_class]
    +     the_link = content_tag(@options[:wrapper_class], the_link, :class => all_wrapper_classes)
    +   end
    +   the_link.html_safe
    - def link_classes
    -   if @html_options[:class]
    -     @html_options[:class] + " #{selected_class}"
    -   elsif !@options[:wrapper]
    -     selected_class
    -   end
    - end
    - def html_options
    -   selected? ? @html_options.merge(:class => link_classes) : @html_options
    - end
    - def link
    -   link_to(@title, @path, html_options)
    - end
    + def unselected_class
    +   @options[:unselected_class] || ''
    + end
    + def class_name_to_be_used
    +   name = selected? ? selected_class : unselected_class
    + end
    - def wrapper_classes
    -   if selected?
    -     "#{selected_class} #{@options[:wrapper_class]}"
    -   else
    -     @options[:wrapper_class]
    -   end
    + def all_wrapper_classes
    +   "#{class_name_to_be_used} #{@options[:other_wrapper_classes]}"
    + end

    And this is a usage example, from a .html.erb file:

    <%= nav_link "Good stuff", good_stuff_path, {}, {selected_class: 'active', unselected_class: 'inactive', wrapper_class: 'li'} %>

    greypants commented Mar 11, 2013

    Thanks for the feedback guys! All good stuff. I'll be moving this into a full repo soon for better collaboration.

    firedev commented Mar 17, 2013

    I think it should be nav_link_to and please make it a gem.

    meal commented Mar 24, 2013

    I tried to used it with engine mounted, unfortunately it doesn't work in such case :(

    +1 @greypants for accepting blocks!

    How can I make this work to ensure the when a user lands on the home-page the home tab is 'selected'?

    Currently no tabs are selected when you land on the website.


    It seems to be running smoothly also on Rails 4, so maybe the Gem dependency could be updated.

    And by the way, thanks for this! :-)

    Just what I needed, and working great on Rails 4! In a similar fashion to how url_segment works, Is it possible to add the selected class to all pages under a sub-directory structure ?

    For example: admin/products/item, admin/products/list etc.. Automatically adding the "selected" class to all urls within "/products" (within the same controller)

    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment