How Can I Create a Dynamic Navigation Menu in Jekyll Using Liquid
Why Should I Use Dynamic Navigation in Jekyll
When your blog grows, maintaining a static menu manually becomes a hassle. Dynamic navigation helps you:
- Keep menus updated automatically when new pages or posts are added
- Reduce the risk of broken links or outdated menus
- Make your theme reusable across multiple sites
What Are the Common Types of Navigation in Jekyll
Most Jekyll themes use one or more of the following navigation structures:
- Top navigation – across the top of the page, usually for pages
- Sidebar navigation – for documentation or categories
- Breadcrumb navigation – to show current position in hierarchy
This guide will show you how to build a top menu and optionally a sidebar.
Step 1: Add Metadata to Pages You Want in the Menu
Edit each page you want in the navigation (e.g., about.md, projects.md, etc.) and add this to their front matter:
nav_order: 1
title: "About"
This will help you sort and label pages in the navigation.
Step 2: Loop Through Pages in Your Layout Using Liquid
In your _includes/header.html or wherever your menu lives, add this:
{% assign pages_list = site.pages | where_exp: "item", "item.nav_order" %}
{% assign sorted_pages = pages_list | sort: "nav_order" %}
{% for page in sorted_pages %}
<li><a href="{{ page.url | relative_url }}">{{ page.title }}</a></li>
{% endfor %}
This will dynamically create links for all pages that have a nav_order defined, sorted by that order.
Step 3: Highlight the Active Page
To highlight the current page in your navigation, add a condition:
<li class="{% if page.url == page.url %}active{% endif %}">
<a href="{{ page.url | relative_url }}">{{ page.title }}</a>
</li>
For better practice, compare the current page.url with the loop item:
<li class="{% if page.url == item.url %}active{% endif %}">
<a href="{{ item.url | relative_url }}">{{ item.title }}</a>
</li>
Step 4: Build Category-Based Navigation from Posts
If you want to generate navigation from blog post categories, you can do:
{% assign categories = site.categories | sort %}
{% for category in categories %}
{% assign category_name = category[0] %}
<li><a href="/categories/{{ category_name | downcase | replace: ' ', '-' }}/">
{{ category_name }}
</a></li>
{% endfor %}
Step 5: Use Data Files for More Control
If you want complete control over the order and labels, use a data file. Create _data/navigation.yml like this:
- title: Home
url: /
- title: About
url: /about/
- title: Blog
url: /blog/
Then render it in your layout:
{% for item in site.data.navigation %}
<li><a href="{{ item.url | relative_url }}">{{ item.title }}</a></li>
{% endfor %}
Step 6: Add Icons or Dropdowns (Optional)
You can add extra keys to your YAML or page front matter such as icon or children and build dropdowns using nested loops. For example:
- title: Projects
url: /projects/
children:
- title: Jekyll Blog
url: /projects/jekyll/
- title: Portfolio Site
url: /projects/portfolio/
Then build the dropdown with nested Liquid for loops.
Common Mistakes to Avoid
- Forgetting to use
relative_urlcan break URLs on GitHub Pages - Relying only on
site.pagesmay include unwanted files (use filters) - Hardcoding navigation makes themes difficult to maintain or scale
How to Style the Menu
Apply classes to the <li> elements and customize using CSS:
ul.nav {
list-style: none;
display: flex;
gap: 1rem;
}
li.active a {
font-weight: bold;
border-bottom: 2px solid #333;
}
Conclusion
Using Liquid to create a dynamic navigation system in Jekyll allows your GitHub Pages site to adapt as your content grows. Whether you're building a simple blog or a full documentation portal, structured navigation improves usability and SEO. Start simple with front matter and evolve to data-driven navigation as your site scales.