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_url can break URLs on GitHub Pages
  • Relying only on site.pages may 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.