Navigation

How to add nested navigation to Shopify themes

By Keir Whitaker | Web Design | Jun 25, 2014

In this article we will discuss how to implement a fully content managed multi-level navigation system within a Shopify theme.

Shopify themes use Liquid, a flexible and powerful template language, to output data from a store into a template. If you aren’t familiar with Liquid, and Shopify theme building in general then I recommend checking out the official Shopify theme documentation.

 

Getting started

If you have ever worked with platforms like WordPress you might be familiar with admin interfaces that allow you to create multi level navigation. When output in a template file these often result in a nested unordered list which, using CSS, we can style into a variety of design patterns. For example a drop down menu or a sidebar menu that reveals more detailed filtering when the top level menus are clicked or hovered over. All approaches give us the ability to offer deeper navigation into our sites without too much complication.

shopify-editions-theme

On first inspecting the navigation function in Shopify you might think that it doesn’t offer us this capability. However armed with a little Liquid knowledge we can take easily achieve multi-level menus in our themes. By using the navigation functionality we can offer our clients an easy way to manage their menus whilst giving us, as theme designers, the desired nested unordered lists for CSS styling.

Our aim in this tutorial is to create a nested unordered list which we can fully control from within the Shopify admin area and for these changes to be reflected in our store.

And here’s the end result:

<ul>
<li><a href="/">Home</a></li>
<li><a href="/collections/cups">Coffee Cups</a>
<ul>
<li><a href="/products/edible-coffee-cup">Edible Coffee Cup</a></li>
<li><a href="/products/moustache-mug">Moustache Mug</a></li>
</ul>
</li>
<li><a href="/collections/all">All Products</a></li>
<li><a href="/pages/about">About</a></li>
<li><a href="/blogs/news">Blog</a></li>
</ul>

Unlike other platforms Shopify doesn’t have the option of having one “super menu” which we can nest our sub-menu items in. That said it’s not a complicated procedure to get this working. By following a simple naming convention it’s possible to generate multi level menu structures.

If you wish to code along with the tutorial the simplest way to do so is to sign up for a free Shopify Partner account and create a free “dev shop”. These are fully featured and allow you to try out your theme before handing it over to your client or launching your own store.

You can try out the code examples in any of the templates in your themes “templates” theme folder, or alternatively I would recommend using the themes default layout file theme.liquid as this will mean your code will appear by default on every page.

 

Creating menus

Let’s begin by creating a new menu, our parent menu, by heading to the Navigation tab in the Shopify admin. In order to create a menu we need to open up the Shopify admin interface and head to the “Navigation” tab on the left hand side.

All stores have a predefined default menu called “Main Menu”. To add items to the list simply click the “add another link” button and give your new item a “link name” and a destination. The select drop down will allow you to easily link to internal sections such as a particular product or collection. Alternatively you can enter your own URL (either internal or external) by choosing “web address” from the options.

navigation-admin

It’s easy to create and edit menus in the Shopify admin.

Once we have this in place we can start to consider the Liquid code we will need to output this in our theme.

In order to output the menu in our theme file we’ll need to know the “handle” of the menu. Handles are unique identifiers within Shopify for products, collections, link lists (the term used for menus in Shopify themes) and pages. They are generally URL safe versions of the name or title of the item. For example Main Menu would result in a handle of main-menu. Whilst they are auto generated you can change them should you wish via the admin interface.

main-menu

Our default main menu features a link to the Coffee Cups collection.

 

Single level navigation

Let’s start by outputting all the items from our “main menu”. In order to do this we can use a simple “for loop” to output all the link list items in turn, here’s how:

<ul>
{% for link in linklists.main-menu.links %}
<li><a href="{{ link.url | escape }}">{{ link.title | escape }}</a></li>
{% endfor %}
</ul>

Let’s have a look at this in more detail. After opening our <ul> we start our Liquid “for loop”. This will output every item contained in the “main-menu” link list. The syntax is a little long here but all we are really doing is assigning the properties of each item in our link list to the variable link. Using this variable we can access the properties of our menu items, in this case url and title.

In order to output the URL and title properties we use the Liquid output syntax of double curly braces, e.g {{ }}. In our example above {{ link.url }} will output the url we entered or generated in the admin and {{ link.title }} will output the text we attributed to the link.

You will have probably noticed the | escape section of code which I haven’t explained. This is a Liquid filter. A filter is a function that changes the input value in a certain way. By way of an example let’s look at the upcase filter:

{{ 'Keir Whitaker' | upcase }}

When output this will render KEIR WHITAKER in our HTML file.

Filters take an input, in this example my name as a text string and change it accordingly. In the case of the upcase filter it transforms the output into uppercase characters. In our example above the escape filter takes the input of URL and title and escapes the text and URL so they are output correctly.

 

Multi level navigation

Now we have the basics under our control we need a way of creating a relationship to our secondary sub-menu. Luckily this isn’t that hard, it just takes a couple more lines of Liquid code. Firstly we need to head back to the Shopify admin and create our sub menu.

We need a way of relating our two menus so our template knows how to output them. Handles come to our rescue once again. It’s not 100% clear initially but every link in addition to the menu itself has a unique handle that we have access to.

Let’s have a look at an example by creating a sub menu from our “Coffee Cups” link. We can safely assume our menu item called “Coffee Cups” auto generated handle will be “coffee-cups”. All you really need to do is remove any punctuation, replace uppercase characters with lowercase and spaces with dashes. If we would like this menu item to have a sub menu we simply make sure that our sub menu also has a handle of “coffee-cups”. It’s worth noting that the title of your menu can be anything, the important factor here is the handle.

sub-menu

Our sub menu has a handle of coffee-cups allowing us to relate our menus together.

 

Now all we need is a little extra Liquid code to help us output the sub menu in our template:

<ul>
{% for link in linklists.main-menu.links %}
<li><a href="{{ link.url| escape }}">{{ link.title | escape }}</a>
{% if linklists[link.handle].links.size > 0 %}
<ul>
{% for sublink in linklists.[link.handle].links %}
<li><a href="{{ sublink.url }}">{{ sublink.title | escape }}</a></li>
{% endfor %}
</ul>
{% endif %} 
</li>
{% endfor %}
</ul>

On first inspection this might be a little daunting. We’ve gone over the majority of it already so let’s examine the new code that’s doing the bulk of the new work for us:

{% if linklists[link.handle].links.size > 0 %}

This appears after the parent menu link is output but before the list items closing </li> tag (line 4 in our code example). The “if statement” checks to see if there is a link list with the same handle as our current link item and if it does exist to see if it has any link items associated with it. If the answer is yes the template opens up a new unordered list and outputs each sub menu item as a list item as per our first example. If the answer is no then the template carries on outputting the next parent menu item.

output-menu

The end result of our code is a nested unordered list of menu items.

 

This check for the existence of a sub menu with the same handle occurs on each and every iteration of the parent menu. You’ll also notice that our “if statement” is closed off with the Liquid code {% end for %} and our “for loop” with {% end for %}. This alerts the template to move on when these have completed their work.

 

Wrapping up

With a little planning it’s actually very easy to create menus that are dependant on each other. Of course you don’t have to output them together – you could if you wish output the sub menu independently anywhere in your template.

I hope this has given you an insight into how easy it is to create versatile menu systems in Shopify themes.

Share this post
Comments (no login required)
  • Matt Clark

    Thanks for the article, I was looking into this just the other day.
    Could you tell me, how would you do this if you wanted a third tier of menu? Could you just repeat the IF statement again in the 2nd UL?

    {% for link in linklists.main-menu.links %}
    {{ link.title | escape }}
    {% if linklists[link.handle].links.size > 0 %}

    {% for sublink in linklists.[link.handle].links %}
    {{ sublink.title | escape }}

    {% if linklists[link.handle].links.size > 0 %}

    {% for sublink in linklists.[link.handle].links %}
    {{ sublink.title | escape }}

    {% endfor %}

    {% endfor %}
    {% endif %}

    {% endfor %}

  • Matt Clark

    Thanks for the article, I was looking into this just the other day.
    Could you tell me, how would you do this if you wanted a third tier of menu? Could you just repeat the IF statement again in the 2nd UL?

    {% for link in linklists.main-menu.links %}
    {{ link.title | escape }}
    {% if linklists[link.handle].links.size > 0 %}

    {% for sublink in linklists.[link.handle].links %}
    {{ sublink.title | escape }}

    {% if linklists[link.handle].links.size > 0 %}

    {% for sublink in linklists.[link.handle].links %}
    {{ sublink.title | escape }}

    {% endfor %}

    {% endfor %}
    {% endif %}

    {% endfor %}

    • http://www.keirwhitaker.com Keir Whitaker

      Hi Mark – great to hear you liked the article. You code example looks spot on. Essentially you just keep repeating the process as many layers deep as you need. As long as you match up the “handles” you should be good to go.

      • Matt Clark

        Thanks Keir!
        I’d been trying to do it on the default theme and even with matched handles, I wasn’t getting the third tier. I’ll look at the code and see if your snippet helps fix the problem. Thanks!

      • http://www.keirwhitaker.com Keir Whitaker

        Hi Matt – Firstly apologies, I called you Mark for some reason. Sorry about that. If it’s not working you should probably change the second sublink to another variable name. It could be getting confused as they are both identical (i.e for the second and third tiers). Hope that helps.

      • Matt Clark

        Haha, Mark is a combo of my first and last name, so I’ll forgive you :P

        I haven’t had the chance to play around yet, but I’ll post the outcome here when I do in case any readers have similar needs. Thanks!