Building custom navigation menus with Navigatrix

Navigatrix is a Ruby library for rendering custom navigation menus in Rails and Sinatra.

Navigation menu requirements rarely change – clicking a navigation menu link should cause the link to render in some sort of “active” state. For example, when visiting http://www.foraker.com/blog, the “Blog” link in the main navigation at the top of the page should be active.

Additionally, we’ll often need to manually activate a link. For example, when visiting the http://www.foraker.com/2013/04 blog archive path, the “Blog” link should appear active, even though the path does not contain /blog.

Navigatrix was designed to make rendering navigations, and activating and deactivating navigation items a breeze.

Installation

To install Navigatrix, add the Navigatrix gem to your Gemfile.

gem 'navigatrix'

And bundle.

bundle install

A simple navigation

Building a simple list-based navigation is trivial with Navigatrix. For example, this configuration:

renders the following HTML navigation, assuming we’re currently on the /home path:

The “Dashboard” nav item is not linked because we are currently on the /home path, and the “Dashboard” <li> receives a class of “active”.

This approach works well for simple navigations, and supports a variety of configuration options. Sometimes, though, a highly customized navigation requires a bolder solution.

Custom navigation requirements

I’ve no earthly idea why anyone would need to do this (perhaps to comply with a UI framework’s markup conventions), but suppose the desired navigation structure looks like this:

Let’s build this wild beast with Navigatrix.

Navigatrix Principles

There are a couple of Navigatrix design principles to explore before we embark on constructing a custom navigation.

1. Separation of configuration and rendering

Configuring a navigation and rendering a navigation are two distinct concerns. Changing the structure of our markup should not require a change to the navigation configuration, and likewise, changing the navigation configuration should not impact the markup.

To satisfy the custom navigation requirements above, our configuration will be:

With the default rendering behavior, we get a basic nested navigation.

Without altering the configuration of this navigation, we can radically alter the generated markup. But first let’s discuss a second design principle, lists vs. list items.

2. Lists are rendered independently of list items.

In a default Navigatrix navigation, a <ul> represents a “list” of navigation items. Each “list item” is represented by an <li>.

Our custom navigation demands a <div><table> wrapper to represent the list, and a <tr> for each list item.

Here is the outer list component of our example:

And here is the outer list item:

Within each outer list item, there is yet another type of list, which we’ll call the nested “inner list.”

And inside the inner list, yet another type of list item.

Creating the outer list

The process of teaching Navigatrix new rendering strategies is known as “registering a renderer“, and we can control the rendering of lists and list items independently.

In order to achieve our custom UI goals, we’ll need to create 4 renderers, 1 renderer for each list type, and renderer 1 for each list item type. In Rails, registering new renderers is typically done in an initializers/navigatrix.rb file.

We can start by registering a renderer for the outer list called “table_list” in initializers/navigatrix.rb.

And then, in the same initializer, register a renderer for the outer list item, called “table_item”.

Creating the inner list

Just as we registered a renderer for the outer list and outer list items, we can also register a renderer for the inner “nested list” and inner “nested list item”.

Rendering the list

Finally, we must specify which renderers to use in our call to render_navigation.

Both the complete source for this example and the Navigatrix docs can be found on github.

Happy navigating!

A brief word of caution: If your navigation needs fall too far afield of standard Navigatrix configuration, it might be time to consider rolling navigation yourself. Off-the-shelf solutions are certainly nice, but can become quite a headache when deviating from the library’s intended use.

Tweet at Foraker

Share this post!