Header

The header template defines the primary site navigation, rendered at the top of every page.

The header template defines the primary site navigation, rendered at the top of every page. Creators manage navigation items in My Site > Header or directly from any site page. The template code lives at partials/menu/index.html.

The terms "header" and "menu" are used interchangeably. In the site builder, creators see "Header". In theme code, the template is in the menu folder and the Liquid object is called menu.

Location

The header template is located at:

partials
└── menu
    └── index.html

The header is a special kind of partial - unlike regular partials, it is automatically rendered at the start of the <body> tag on every page. You don't need to include it manually.

Schema

The header template supports a schema defined in YAML front matter, just like blocks. The schema defines both the navigation configuration and any custom variables that creators can set.

The following front matter keys control navigation behaviour:

KeyTypeDefaultDescription
max_item_levelsinteger (1–3)1The maximum depth of nested navigation items. Set to 2 or 3 to allow dropdown/sub-navigation in the site builder.
supports_open_new_tabbooleanfalseWhen true, creators can toggle "Open in a new tab" on individual navigation items.
cachebooleantrueControls whether the header is cached. Set to false for dynamic content like cart item counts.

Custom variables

Any variables defined in the attributes block are accessible on the menu object using dot notation:

---
max_item_levels: 2
supports_open_new_tab: true
cache: false
attributes:
  background_colour:
    type: color
    label: Background colour
  logo:
    type: image
    label: Logo
  cta_text:
    type: string
    label: CTA button text
  cta_link:
    type: link
    label: CTA button link
---
<header style="background-color: {{ menu.background_colour }};">
  {% if menu.logo %}
    <img src="{{ menu.logo.url }}" alt="{{ menu.logo.alt }}">
  {% endif %}

  {% if menu.cta_text and menu.cta_link %}
    <a href="{{ menu.cta_link.url }}">{{ menu.cta_text }}</a>
  {% endif %}
</header>

The menu object

The menu Liquid object is available inside the header template. It exposes:

PropertyTypeDescription
itemsarrayThe top-level navigation items, sorted by position. Each item may contain nested items.
(custom attributes)variesAny variable defined in the schema — accessed via menu.variable_name.

Each item in menu.items exposes the following properties:

PropertyTypeDescription
labelstringThe display text for the navigation item.
urlstringThe resolved URL. Blank for folder items.
new_tabbooleanWhether the link should open in a new tab. Only configurable by creators when supports_open_new_tab is true.
itemsarrayChild navigation items. Empty if the item has no children.
linked_resourcedropThe linked page, product, or blog post object (as a Liquid drop). nil for external links and folders.

Folder items have a blank url — they exist only as parent containers for child items. In your template, render them as non-clickable labels or toggle buttons rather than links.

Template context

In addition to the menu object, the header template has access to the following objects:

ObjectDescription
companyThe current company.
cartThe current shopping cart.
userThe currently logged-in user, if any.
pageThe current HTTP request (path, URL, host, params, locale).
searchThe search object.

Rendering navigation

Basic navigation

The essential code to render a 2-level navigation:

---
max_item_levels: 2
supports_open_new_tab: true
---
<nav>
  {% for item in menu.items %}
    <a href="{% if item.url != '' %}{{ item.url }}{% else %}javascript:void(0);{% endif %}"
      {% if item.new_tab %}target="_blank" rel="noopener noreferrer"{% endif %}>
      {{ item.label }}
    </a>

    {% if item.items.size > 0 %}
      <ul>
        {% for nested_item in item.items %}
          <li>
            <a href="{% if nested_item.url != '' %}{{ nested_item.url }}{% else %}javascript:void(0);{% endif %}"
              {% if nested_item.new_tab %}target="_blank" rel="noopener noreferrer"{% endif %}>
              {{ nested_item.label }}
            </a>
          </li>
        {% endfor %}
      </ul>
    {% endif %}
  {% endfor %}
</nav>

Handling folder items

Folder items have no URL and act as parent containers. You can detect them by checking for a blank url:

{% for item in menu.items %}
  {% if item.url != '' %}
    <a href="{{ item.url }}">{{ item.label }}</a>
  {% else %}
    <button type="button">{{ item.label }}</button>
  {% endif %}
{% endfor %}

Using linked_resource

The linked_resource property gives you access to the full Liquid drop for the linked page, product, or blog post. This is useful for displaying additional information beyond the label:

{% for item in menu.items %}
  {% if item.linked_resource %}
    <a href="{{ item.url }}">
      {{ item.label }}
      {% if item.linked_resource.type == 'experience' %}
        <span>from {{ item.linked_resource.display_price }}</span>
      {% endif %}
    </a>
  {% endif %}
{% endfor %}

Caching

By default, the header template is cached so it renders quickly. The cached version is served for all requests until the cache is invalidated (e.g. when the header content or settings are updated).

Dynamic content

If your header needs content that changes per request — such as a cart item count or user-specific greeting — set cache: false in the front matter:

---
cache: false
---

With caching disabled, the entire header is rendered on every request. To maintain performance, wrap the static parts of your header (logo, navigation links, CTA buttons) in a cache tag:

{% cache menu %}
  <div class="header__logo">
    <img src="{{ menu.logo.url }}" alt="{{ menu.logo.alt }}">
  </div>

  <nav>
    {% for item in menu.items %}
      <a href="{{ item.url }}">{{ item.label }}</a>
    {% endfor %}
  </nav>
{% endcache %}

<div class="header__cart">
  <span>{{ cart.total_items }}</span>
</div>

This approach gives you the best of both worlds: static content is cached for performance, while dynamic content is rendered fresh on every request.

Hiding the header

The header can be hidden entirely for specific use cases (e.g. landing pages or checkout flows). When hidden, the header template is not rendered and no HTML is output. This is controlled at the platform level, not in the theme template.

On this page