Recipes

Navigation Tree

How to create a navigation tree for your Shopify store in Nuxt

Sometimes you need a navigation so your users know where to go. This recipe will show you how to create a navigation tree for your Shopify store in Nuxt.

In order to follow this recipe, you need to have a navigation menu set up in your Shopify store. Make sure you have configured the Nuxt Shopify module and have a Shopify store set up.

Obtaining the Menu Data

First, we create a new component components/NavigationTree.vue. Here, we use the useAsyncStorefront composable to fetch the navigation menu from Shopify. To do this, we use a GraphQL query to get the menu items by their handle (e.g., main-menu):

query GetNavigation($handle: String!) {
  menu(handle: $handle) {
    items {
      ... on MenuItem {
        title
        url
      }
    }
  }
}

When using this query with the useAsyncStorefront composable, we pass the handle as a variable:

~/components/NavigationTree.vue
<script setup lang="ts">
const { data } = await useAsyncStorefront('menu', `#graphql
  query GetNavigation($handle: String!) {
    menu(handle: $handle) {
      items {
        ... on MenuItem {
          title
          url
        }
      }
    }
  }
`, {
  variables: {
    handle: 'main-menu',
  },
})
</script>

<template>
  <pre>{{ data }}</pre>
</template>

The result will be a list of menu items with their titles and URLs.

Transforming the Data

The data returned from the query is a good starting point, but we want to transform the data into a format that can be used by a navigation component, in this case the Nuxt UI UNavigationMenu. To do so, we can use the transform option of useAsyncData, which is built into the useAsyncStorefront composable:

~/components/NavigationTree.vue
<script setup lang="ts">
const { data: items } = await useAsyncStorefront('menu', `#graphql
  query GetNavigation($handle: String!) {
    menu(handle: $handle) {
      items {
        ... on MenuItem {
          title
          url
        }
      }
    }
  }
`, {
  variables: {
    handle: 'main-menu',
  },
}, {
  transform: data => data.menu?.items?.map(item => ({
    label: item.title,
    to: item.url,
  })) ?? [],
})
</script>

<template>
  <UNavigationMenu :items="items" />
</template>

And that's it! You now have a navigation tree that fetches its data from Shopify. If you want to use a different menu, just change the handle variable to the handle of your desired menu in Shopify.

Making the Component Reusable

At this point, it might also make sense to add a handle prop to the component so you can reuse it for different menus. We can also translate the menu to a specific language and country by using an @inContext directive in the GraphQL query:

query GetNavigation($handle: String!, $language: LanguageCode, $country: CountryCode)
@inContext(handle: $handle, language: $language, country: $country) {
  menu(handle: $handle) {
    items {
      ... on MenuItem {
        title
        url
      }
    }
  }
}

When adding all of this together, we get the final component. You can use this component anywhere in your app, just make sure to pass the correct handle:

~/components/NavigationTree.vue
<script setup lang="ts">
const props = defineProps<{
  handle: string
}>()

const key = computed(() => `menu-${props.handle}`)

const { data: items } = await useAsyncStorefront(key, `#graphql
  query GetNavigation($handle: String!, $language: LanguageCode, $country: CountryCode)
  @inContext(handle: $handle, language: $language, country: $country) {
    menu(handle: $handle) {
      items {
        ... on MenuItem {
          title
          url
        }
      }
    }
  }
`, {
  variables: {
    handle: props.handle,
    language: 'EN',
    country: 'US',
  },
}, {
  transform: data => data.menu?.items?.map(item => ({
    label: item.title,
    to: item.url,
  })) ?? [],
})
</script>

<template>
  <UNavigationMenu :items="items" />
</template>