Global UI: Header and Navigation Beginner 10 min read

Navigation Liquid Objects and Linklists

Master the Liquid objects for navigation, including linklists, links, and how to detect active states for building dynamic menus.

Shopify exposes menu data through Liquid objects. Understanding these objects is essential for building navigation components that reflect the menus merchants create in the admin.

The linklists Object

The linklists object contains all menus defined in the store. Access a specific menu by its handle:

{{ linklists['main-menu'] }}
{{ linklists['footer'] }}
{{ linklists.main-menu }}

Both bracket notation and dot notation work, but bracket notation is safer for handles with hyphens.

Each linklist (menu) has these properties:

{%- assign menu = linklists['main-menu'] -%}
{{ menu.title }} {# "Main menu" #}
{{ menu.handle }} {# "main-menu" #}
{{ menu.levels }} {# Number of nesting levels (1-3) #}
{{ menu.links }} {# Array of link objects #}
{{ menu.links.size }} {# Number of top-level links #}

Each linklist contains a links array with link objects:

{%- for link in linklists['main-menu'].links -%}
{{ link.title }}
{%- endfor -%}

Each link object provides:

PropertyDescriptionExample
titleDisplay text”About Us”
urlLink destination”/pages/about”
handleURL-safe identifier”about-us”
activeTrue if current pagetrue or false
child_activeTrue if a child is activetrue or false
currentTrue if exact URL matchtrue or false
linksArray of child links[...]
levelsNesting depth below this link0, 1, or 2
typeLink type”collection_link”, “page_link”, etc.
objectThe linked objectCollection, product, page, etc.

Basic Navigation Loop

Here’s a simple navigation rendering:

<nav>
<ul class="nav-list">
{%- for link in linklists['main-menu'].links -%}
<li class="nav-item">
<a href="{{ link.url }}" class="nav-link">
{{ link.title }}
</a>
</li>
{%- endfor -%}
</ul>
</nav>

Active States

Use active and child_active to highlight the current section:

{%- for link in linklists['main-menu'].links -%}
<li class="nav-item">
<a
href="{{ link.url }}"
class="nav-link {% if link.active %}is-active{% endif %}"
{% if link.active %}aria-current="page"{% endif %}
>
{{ link.title }}
</a>
</li>
{%- endfor -%}

Understanding Active States

PropertyWhen it’s true
activeThe current page matches this link’s URL
child_activeA descendant link matches the current page
currentExact URL match (stricter than active)

Example scenarios on /collections/shirts:

Link URLactivechild_active
/collections/shirtstruefalse
/collectionstruefalse
/ (Home)falsefalse
”Shop” (parent of Shirts)falsetrue

Nested Navigation

Handle child links for dropdown menus:

<nav>
<ul class="nav-list">
{%- for link in linklists['main-menu'].links -%}
<li class="nav-item {% if link.links.size > 0 %}has-dropdown{% endif %}">
<a
href="{{ link.url }}"
class="nav-link {% if link.active or link.child_active %}is-active{% endif %}"
>
{{ link.title }}
{%- if link.links.size > 0 -%}
<span class="dropdown-icon">▼</span>
{%- endif -%}
</a>
{%- if link.links.size > 0 -%}
<ul class="dropdown-menu">
{%- for child_link in link.links -%}
<li class="dropdown-item">
<a
href="{{ child_link.url }}"
class="dropdown-link {% if child_link.active %}is-active{% endif %}"
>
{{ child_link.title }}
</a>
</li>
{%- endfor -%}
</ul>
{%- endif -%}
</li>
{%- endfor -%}
</ul>
</nav>

Three-Level Nesting

For deeply nested menus:

{%- for link in linklists['main-menu'].links -%}
<div class="nav-item">
<a href="{{ link.url }}">{{ link.title }}</a>
{%- if link.links.size > 0 -%}
<div class="submenu">
{%- for child in link.links -%}
<div class="submenu-item">
<a href="{{ child.url }}">{{ child.title }}</a>
{%- if child.links.size > 0 -%}
<div class="submenu-level-2">
{%- for grandchild in child.links -%}
<a href="{{ grandchild.url }}">{{ grandchild.title }}</a>
{%- endfor -%}
</div>
{%- endif -%}
</div>
{%- endfor -%}
</div>
{%- endif -%}
</div>
{%- endfor -%}

The link.type property tells you what kind of link it is:

{%- case link.type -%}
{%- when 'collection_link' -%}
{# Link to a collection #}
{%- when 'product_link' -%}
{# Link to a product #}
{%- when 'page_link' -%}
{# Link to a page #}
{%- when 'blog_link' -%}
{# Link to a blog #}
{%- when 'article_link' -%}
{# Link to a blog article #}
{%- when 'policy_link' -%}
{# Link to a policy page #}
{%- when 'http_link' -%}
{# External or custom URL #}
{%- when 'frontpage_link' -%}
{# Link to homepage #}
{%- when 'catalog_link' -%}
{# Link to /collections/all #}
{%- endcase -%}

Accessing the Linked Object

For collection and product links, access the underlying object:

{%- for link in linklists['main-menu'].links -%}
{%- if link.type == 'collection_link' -%}
{# Access the collection object #}
<a href="{{ link.url }}">
{{ link.title }}
<span>({{ link.object.products_count }} products)</span>
</a>
{# Show collection image #}
{%- if link.object.image -%}
<img src="{{ link.object.image | image_url: width: 200 }}" alt="">
{%- endif -%}
{%- else -%}
<a href="{{ link.url }}">{{ link.title }}</a>
{%- endif -%}
{%- endfor -%}

This is powerful for megamenus that show collection images or product counts.

Using Theme Settings for Menu Selection

Let merchants choose which menu to use:

{
"type": "link_list",
"id": "menu",
"label": "Menu",
"default": "main-menu"
}
{%- assign menu = linklists[section.settings.menu] -%}
{%- for link in menu.links -%}
<a href="{{ link.url }}">{{ link.title }}</a>
{%- endfor -%}

Checking if Menu Exists

Guard against missing menus:

{%- assign menu = linklists[section.settings.menu] -%}
{%- if menu.links.size > 0 -%}
<nav>
{%- for link in menu.links -%}
<a href="{{ link.url }}">{{ link.title }}</a>
{%- endfor -%}
</nav>
{%- else -%}
{# Fallback or empty state #}
{%- endif -%}

Detect and handle external links:

{%- for link in menu.links -%}
{%- assign is_external = false -%}
{%- if link.url contains 'http' and link.url contains '://' -%}
{%- unless link.url contains shop.domain -%}
{%- assign is_external = true -%}
{%- endunless -%}
{%- endif -%}
<a
href="{{ link.url }}"
{% if is_external %}target="_blank" rel="noopener noreferrer"{% endif %}
>
{{ link.title }}
{%- if is_external %} <span class="external-icon">↗</span>{%- endif -%}
</a>
{%- endfor -%}

Complete Navigation Snippet

Here’s a reusable navigation snippet:

{# snippets/navigation.liquid #}
{# Usage: {% render 'navigation', menu: linklists['main-menu'], class: 'header-nav' %} #}
{%- if menu.links.size > 0 -%}
<nav class="{{ class }}" aria-label="{{ menu.title }}">
<ul class="{{ class }}__list">
{%- for link in menu.links -%}
{%- liquid
assign has_children = false
if link.links.size > 0
assign has_children = true
endif
assign is_active = false
if link.active or link.child_active
assign is_active = true
endif
-%}
<li class="{{ class }}__item {% if has_children %}has-dropdown{% endif %}">
<a
href="{{ link.url }}"
class="{{ class }}__link {% if is_active %}is-active{% endif %}"
{% if link.active %}aria-current="page"{% endif %}
{% if has_children %}aria-expanded="false" aria-haspopup="true"{% endif %}
>
{{ link.title }}
</a>
{%- if has_children -%}
<ul class="{{ class }}__dropdown">
{%- for child in link.links -%}
<li class="{{ class }}__dropdown-item">
<a
href="{{ child.url }}"
class="{{ class }}__dropdown-link {% if child.active %}is-active{% endif %}"
{% if child.active %}aria-current="page"{% endif %}
>
{{ child.title }}
</a>
</li>
{%- endfor -%}
</ul>
{%- endif -%}
</li>
{%- endfor -%}
</ul>
</nav>
{%- endif -%}

Practice Exercise

Create a footer navigation that:

  1. Renders menu columns from nested links
  2. Shows parent titles as column headings
  3. Lists child links below
<footer class="footer">
<div class="footer-nav">
{%- for column in linklists['footer'].links -%}
<div class="footer-column">
<h4 class="footer-column__heading">{{ column.title }}</h4>
{%- if column.links.size > 0 -%}
<ul class="footer-column__list">
{%- for link in column.links -%}
<li>
<a href="{{ link.url }}">{{ link.title }}</a>
</li>
{%- endfor -%}
</ul>
{%- else -%}
{# If no children, link the heading #}
<a href="{{ column.url }}">View {{ column.title }}</a>
{%- endif -%}
</div>
{%- endfor -%}
</div>
</footer>

Key Takeaways

  1. linklists['handle'] accesses menus by handle
  2. link.links contains child links for nesting
  3. link.active is true when on the linked page
  4. link.child_active is true when a descendant is active
  5. link.type identifies the link type (collection, page, etc.)
  6. link.object gives access to the linked resource
  7. Use settings to let merchants choose menus
  8. Always check if menu exists before rendering

What’s Next?

Now that you understand navigation Liquid objects, the next lesson covers Building the Header Section where we’ll put everything together into a complete header component.

Finished this lesson?

Mark it complete to track your progress.

Discussion

Loading comments...