Jekyll navigation based on data file
This post is archived. Opinions expressed herein may no longer represent my current views. Links, images and other media might not work as intended. Information may be out of date. For further questions contact me.
When I first moved from WordPress to Jekyll I found the process of creating a navigation menu somewhat counter-intuitive. Different theme authors would implement their own solution, while the official documentation offers an approach that falls short of being the golden standard. After a few trials and errors I have settled with a method that makes use of a data file and some simple Liquid logic to produce a list of links.
So let me show you how I do it.
Under the _data
directory I create a new file called menu.yml
. In it I add the structured data of my menu items. In this case that is their name/label and URL slug.
# menu.yml contents
- name: About
url: "/author/"
- name: Contact
url: "/contact/"
- name: Codelog
url: "/codelog/"
- name: Prot16
url: "/schemes/"
- name: Akademos
url: "/akademos/"
Once I have the data structure in place, I create a navigation.html
file under the _includes
directory. This is where I insert the Liquid logic to create the menu based on the data.
<!-- navigation.html contents -->
{% assign links = site.data.menu %}
<ul>
{% for link in links %}
<li>
<a href="{{ link.url }}">
{{ link.name }}
</a>
</li>
{% endfor %}
</ul>
What this code snippet does is instruct the system to generate an unordered list and add list items with the name
and url
assigned to them in the menu.yml
.
The resulting HTML would be:
<ul>
<li>
<a href="/author/">
Author
</a>
</li>
<li>
<a href="/contact/">
Contact
</a>
</li>
<li>
<a href="/codelog/">
Codelog
</a>
</li>
<li>
<a href="/schemes/">
Prot16
</a>
</li>
<li>
<a href="/akademos/">
Akademos
</a>
</li>
</ul>
I can then use Liquid to include the navigation.html
in my page templates, more specifically in the header section.
<header class="site-header">
<!-- Some code goes here -->
<nav class="site-nav">
{% include navigation.html %}
</nav>
</header>
Then proceed to add some styles to make it look as intended. Here’s a sample in SCSS:
.site-nav {
ul {
font-size: $h7;
list-style-type: none;
padding: 0;
margin: 0;
@include desktop {
font-size: $h6;
}
}
li {
display: inline-block;
margin-right: $sone;
line-height: $sone * 2;
&:last-child {
margin-right: 0;
}
@include tablet {
line-height: $vrythm;
}
@include desktop {
margin-right: $sone * 1.25;
}
}
}
And we are done!
At this point I should note that I prefer to manually insert my own URL slug. In each page’s front matter, I define it using the permalink
key. Example:
---
title: Contact information
permalink: /contact/
---
Why this approach?
For a few reasons:
- Ease of use. It makes it very easy to change menu items. Just modify the
menu.yml
file under_data
. Liquid will do the rest. With a minimum of training even non-technical users can have a menu up and running. - List of links, not pages. Some solutions I have come across instruct Jekyll to produce a list of items specified as “pages”. Whereas here the system will just use links. Any web page may be included, both internal and external ones, all without them qualifying as “page” in the technical/Jekyll sense.
- Agnostic to directory structure. The combination of data and Liquid is abstract enough that it does not impose any constraint on the overall structure of the directories. The menu items can be scattered across multiple folders and still cause no problems whatsoever. Obviously it is a good idea to keep things organised, but that should not be up to Liquid to decide.
- Sorted as required. The Liquid loop I presented above does not use any filter to sort the menu items. It will produce the list of links in the exact order they appear in their data file. A different sequence can be achieved using simple Liquid filters. One may even add more objects to the
_menu.yml
and use those to organise the list. Since my menu is short, I prefer to keep things simple. - The label is not a title. The labels displayed in the menu, e.g. “Contact”, are not the actual title of the page they reference. The latter may be several words long, while the navigation module can be kept concise and tidy. By decoupling the two, I can write titles that serve the purpose of a title and menu labels that are specific to that task. All thanks to the inclusion of the
name
key in themenu.yml
.
The same approach can be applied to a range of cases. One closely related to the above is a list of social media accounts, each using the corresponding brand icon. That needs a tiny bit more on the styling part, but is otherwise identical.
This is how I have decided to implement the navigation module in Akademos, my upcoming theme for Jekyll-powered websites. I explain these technicalities in the installation guide. Hopefully users will find the approach to be functional and adaptable.