Published on
← Blog

Build dynamic navs with current_page? (and conditional classes)

Authors

By combining the current_page? helper with dynamic classes, we can build a nav that's styled differently based on the page we're on. Here's an example —

Dynamic nav in a Ruby on Rails app, using the current_page? and class_names helper methods.
Dynamic nav in a Ruby on Rails app, using the current_page? and class_names helper methods.
Table of Contents

Nav bars. You've got one, I've got one — almost every Rails app has a nav.

I've finally built a nav I'm happy with, and I wanted to share it with you all! This nav combines the current_page? helper with dynamic classes (under the hood, the class_names helper).

The final result? Conditional styling based on the page we're on. In this example, we use this to style our "active" page with a red underline.

Before we get to the example, I want to look at the current_page? helper, and how conditional classes work in our Rails views.

The current_page? helper

current_page? is a helper method defined inside ActionView::Helpers::UrlHelper (view the source).

We pass it a path or URL, and it returns true if we're on that page.

For example, if we open our Rails app and visit the root_path of our app, current_path?(root_path) will be true.

The current_path? helper takes a few more optional arguments, but we've covered the crux of it. I'm sure it has a wide range of uses, but I typically use it for adding dynamic styles to my UIs (like we're doing with this navbar).

Conditional classes (using the class_names helper)

The class_names helper (itself just an alias for ActionView::Helpers::TagHelper.token_list, source) was introduced as part of Rails 6.1 to make conditionally applying classes in your views easier.

We can use it implicitly in our views like this —

# class-1 applied, class-2 not
<%= tag.p class: [
  "class-1" : true,
  "class-2" : false
] %>

# output
<p class="class-1"></p>

We can use the class_names helper (implicitly) with any ActionView helper that accepts a class: attribute (tag helpers like tag.p, link_to like below, and more). We can even use it inside ViewComponents!

The advantage of the class_names helper is cleaner view code — rather than interpolating classes inside your views (<p class="<%= "class-1" if true %>">), we let Rails handle everything.

Building a dynamic navbar

Here's the nav! Combining current_path? with the class_names helper (implicitly) inside our link_to tag makes dynamically applying classes easy.

The first group of classes inside our link_to are always applied. The second group are applied if we're on the current path (current_path?(...) == true), otherwise the third group apply (!current_path?(...) == true).

nav.erb
<% @nav_links = [
    { name: "Pricing", path: pricing_path },
    { name: "Components", path: components_path },
    { name: "Docs", path: documentation_path },
  ] %>

<% @nav_links.each do |link| %>
  <%= link_to link[:name],
              link[:path],
              class: [
                "border-b-2 px-1 hover:border-red-500",
                "border-red-500": current_page?(link[:path]),
                "border-transparent text-gray-700": !current_page?(link[:path]),
              ] %>
<% end %>

You can expand and adjust this as needed, but it's a perfect starting point for a simple, dynamic nav for your Ruby on Rails app.

Here's the same code packaged up as a ViewComponent. I've been diving into ViewComponents lately and I'm loving them. If you prefer regular partials though, you can use the code from above.

components/ui/nav.rb
class Ui::Nav < ViewComponent::Base
  def before_render
    @nav_links = [
      {name: "Pricing", path: pricing_path},
      {name: "Components", path: components_path},
      {name: "Docs", path: documentation_path},
    ]
  end

  erb_template <<~ERB
    <nav>
      <% @nav_links.each do |link| %>
        <%= link_to link[:name],
                    link[:path],
                    class: [
                      "border-b-2 px-1 hover:border-red-500",
                      "border-red-500": current_page?(link[:path]),
                      "border-transparent text-gray-700": !current_page?(link[:path]),
                    ] %>
      <% end %>
    </nav>
  ERB
end

Note: We call def before_render instead of def initialize since we need access to the path helpers. Learn more from the ViewComponent docs.

Conclusion

I hope you found this article useful! This dynamic nav is something I pulled out of RailsNotes UI, and initially started as a tweet. People seemed to enjoy it, so I thought I'd turn it into something less transient.

I also hope you've learned something new! The current_path? helper is one I use all the time, and class_names is also growing on me. Both are super handy!

Join 1050+ Ruby on Rails lovers

Join 1050+ Ruby on Rails lovers and get our weekly newsletter, no spam. Just interesting Rails articles, from here and around the web.