Skip to main content

Navigation Menu

A navigation menu allows your customers to navigate your store and find the products they are looking for.

Typically, navigation is based on a hierarchy of collections. We can get the top-level collections using the collections query with the topLevelOnly filter:

Graphql
query GetTopLevelCollections {  collections(options: { topLevelOnly: true }) {    items {      id      slug      name      featuredAsset {        id        preview      }    }  }}

Building a navigation tree

The collections query returns a flat list of collections, but we often want to display them in a tree-like structure. This way, we can build up a navigation menu which reflects the hierarchy of collections.

First of all we need to ensure that we have the parentId property on each collection.

Shop API
query GetAllCollections {  collections(options: { topLevelOnly: true }) {    items {      id      slug      name      parentId      featuredAsset {        id        preview      }    }  }}

Then we can use this data to build up a tree structure. The following code snippet shows how this can be done in TypeScript:

src/utils/array-to-tree.ts
export type HasParent = { id: string; parentId: string | null };export type TreeNode<T extends HasParent> = T & {    children: Array<TreeNode<T>>;};export type RootNode<T extends HasParent> = {    id?: string;    children: Array<TreeNode<T>>;};/** * Builds a tree from an array of nodes which have a parent. * Based on https://stackoverflow.com/a/31247960/772859, modified to preserve ordering. */export function arrayToTree<T extends HasParent>(nodes: T[]): RootNode<T> {    const topLevelNodes: Array<TreeNode<T>> = [];    const mappedArr: { [id: string]: TreeNode<T> } = {};    // First map the nodes of the array to an object -> create a hash table.    for (const node of nodes) {        mappedArr[node.id] = { ...(node as any), children: [] };    }    for (const id of nodes.map((n) => n.id)) {        if (mappedArr.hasOwnProperty(id)) {            const mappedElem = mappedArr[id];            const parentId = mappedElem.parentId;            if (!parent) {                continue;            }            // If the element is not at the root level, add it to its parent array of children.            const parentIsRoot = !mappedArr[parentId];            if (!parentIsRoot) {                if (mappedArr[parentId]) {                    mappedArr[parentId].children.push(mappedElem);                } else {                    mappedArr[parentId] = { children: [mappedElem] } as any;                }            } else {                topLevelNodes.push(mappedElem);            }        }    }    const rootId = topLevelNodes.length ? topLevelNodes[0].parentId : undefined;    return { id: rootId, children: topLevelNodes };}

Live example

Here's a live demo of the above code in action:

Was this chapter helpful?
Report Issue
Edited Feb 2, 2026·Edit this page