Build a Navigation Menu From Jekyll Data
Hard-coding the same navigation in several layouts makes small changes unnecessarily annoying. Jekyll data files give the links one home, while an include handles the HTML.
Create _data/nav.yml with the menu structure:
- title: "Home"
href: "/"
- title: "About"
href: "/about/"
- title: "Projects"
href: "/projects/"
subcategories:
- subtitle: "Project1"
subhref: "#"
- subtitle: "Project2"
subhref: "#"
The filename matters. A file named nav.yml becomes available as site.data.nav in Liquid.
Next, create _includes/navigation.html:
<nav aria-label="Primary">
<ul>
{% for nav in site.data.nav %}
{% if nav.subcategories != null %}
<li>
<a href="{{ site.url }}{{ nav.href }}">{{ nav.title }} ▼</a>
<ul>
{% for subcategory in nav.subcategories %}
<li>
<a href="{{ site.url }}{{ subcategory.subhref }}">
{{ subcategory.subtitle }}
</a>
</li>
{% endfor %}
</ul>
</li>
{% elsif nav.title == page.title %}
<li class="active">
<a href="{{ nav.href }}">{{ nav.title }}</a>
</li>
{% else %}
<li>
<a href="{{ site.url }}{{ nav.href }}">{{ nav.title }}</a>
</li>
{% endif %}
{% endfor %}
</ul>
</nav>
The loop renders an ordinary link unless the item contains subcategories, in which case it adds a nested list. It also gives the matching page an active class for styling.
Include the menu wherever the layout needs it:
{% include navigation.html %}
For a small site, this is enough. If navigation grows more complex, I would keep the data file but move state detection away from title matching—titles can change, while a dedicated identifier is much less surprising.