Navigation menus
Merchant-built menus, resolved to localized links a theme just renders.
Navigation is data. A merchant builds named menus in the dashboard; the server resolves every link to a language-prefixed URL and a localized label before render, so a theme renders navigation without ever hardcoding a route or looking up a slug.
The menus global
Every page receives menus — the tenant’s menus resolved and keyed by handle. Each
item carries a ready url, label, and children:
{% if menus.main.items.size > 0 %}
{% for item in menus.main.items %}
{% if item.children.size > 0 %}
<div class="dropdown">
<a href="{{ item.url }}">{{ item.label }}</a>
<div class="submenu">
{% for child in item.children %}
<a href="{{ child.url }}">{{ child.label }}</a>
{% endfor %}
</div>
</div>
{% else %}
<a href="{{ item.url }}">{{ item.label }}</a>
{% endif %}
{% endfor %}
{% endif %}
main and footer are the conventional handles. Prefer a menu when present and
fall back to navCategories (the store’s top categories) so a fresh store that
never built a menu still has a working header:
{% if menus.main.items.size > 0 %}
{# render menus.main … #}
{% elsif navCategories.size > 0 %}
{# render navCategories … #}
{% else %}
{# static Home / Products / Categories #}
{% endif %}
A menu setting
Let a merchant choose which menu a section uses with a menu setting type — its
value is a handle, resolved to the same tree shape as a menus.* entry:
{ "type": "menu", "name": "footer_menu", "label": { "en": "Footer menu" } }
{% for item in component.footer_menu.items %}
<a href="{{ item.url }}">{{ item.label }}</a>
{% endfor %}
Resolved link shape
Each item the server hands you:
| Field | What it is |
|---|---|
url |
Language-prefixed, ready to use in href. |
label |
Localized text (the merchant’s label, or the linked resource’s own name). |
type |
home, collections, blog, category, product, page, blog_post, or url. |
children |
Nested items (same shape). |
Dead links disappear A link to a resource that was deleted or unpublished (a removed product, an unpublished page) resolves to nothing and is dropped from the tree — a stale menu never renders a broken link, so you don’t need to guard for it.