Skip to main content

Create Dynamic Menus in Storyblok and Nuxt

Contents
    Try Storyblok

    Storyblok is the first headless CMS that works for developers & marketers alike.

    In this part of the tutorial series, we will make the menu in our header component dynamic, so that you can manage it directly from Storyblok.

    Live demo:

    If you’re in a hurry, have a look at our live demo in Stackblitz! Alternatively, you can explore or fork the code from the Nuxt Ultimate Tutorial GitHub Repository.

    Requirements

    This tutorial is part 3 of the Ultimate Tutorial Series for Nuxt. We recommend that you follow the previous tutorials before starting this one.

    Setup in Storyblok

    First, we will have to create a new content type component wherein our menu entries can be stored. In order to do that, go to the Block Library {1} and create a New block {2}.

    app.storyblok.com
    Creating a new block in the Block Library
    1
    2

    Creating a new block in the Block Library

    Enter the name config {1} and choose Content type block {2}.

    app.storyblok.com
    Creating a content type block
    1
    2

    Creating a content type block

    Now you can create a new field with the name header_menu {1} and choose the field type Blocks {2}.

    app.storyblok.com
    Creating a field for the header menu
    1
    2

    Creating a field for the header menu

    In this field, we would like to provide the possibility to add menu links as nested blocks. To accomplish that, let’s create another new block. This time it should be a Nested block {1} with the name menu_link {2}.

    app.storyblok.com
    Creating a nested block
    1
    2

    Creating a nested block

    Now we can add a new field called link {1} in this newly created block and choose Link as the field type {2}.

    app.storyblok.com
    Creating a link field
    1
    2

    Creating a link field

    Alright, our component schemas are almost done! Just one more step: to avoid that just any block could be nested in our header_menu, we want to make sure that only specific components can be inserted {1}. Now you can choose the menu_link block in the whitelist {2}.

    app.storyblok.com
    Allowing only specific components to be inserted
    1
    2

    Allowing only specific components to be inserted

    With that out of the way, we can now go to the Content section of our Storyblok space. Here, we want to create a new story with the name Config {2}, using our recently created content type Config {3}.

    app.storyblok.com
    Creating a new Config story
    1
    2

    Creating a new Config story

    If you open this newly created Config story, you can now nest as many menu_link blocks in the header_menu field as you would like. For now, let’s add our Blog and About page.

    learn:

    Note that the components you put in the storyblok folder, and will be imported dynamically using <StoryblokComponent />, must be named with a hyphen in your Storyblok space: ‘example-component’ and with Pascal case in your project: ‘ExampleComponent’.

    app.storyblok.com
    Dynamic menu rendered correctly in Nuxt

    Dynamic menu rendered correctly in Nuxt

    Rendering the Menu in Nuxt

    Having taken care of our setup in Storyblok, we can now turn to the code and implement our dynamic menu in the frontend. To accomplish that, let’s update components/Header.vue with the following code:

    components/Header.vue
    <script setup>
    const storyblokApi = useStoryblokApi()
    const { data } = await storyblokApi.get('cdn/stories/config', {
      version: 'draft',
      resolve_links: 'url',
    })
    
    const headerMenu = ref(null)
    headerMenu.value = data.story.content.header_menu
    </script>
    
    <template>
      <header class="w-full h-24 bg-[#f7f6fd]">
        <div class="container h-full mx-auto flex items-center justify-between">
          <NuxtLink to="/">
            <h1 class="text-[#50b0ae] text-3xl font-bold">Storyblok Nuxt</h1>
          </NuxtLink>
          <nav v-if="headerMenu">
            <ul class="flex space-x-8 text-lg font-bold">
              <li v-for="blok in headerMenu" :key="blok._uid">
                <NuxtLink :to="blok.link.cached_url" class="hover:text-[#50b0ae]">
                  {{ blok.link.story.name }}
                </NuxtLink>
              </li>
            </ul>
          </nav>
        </div>
      </header>
    </template>

    If you go back to the Visual Editor now, you can see your menu being rendered correctly. Feel free to experiment a little bit with it by adding or reordering the entries and saving the Config story.

    Fantastic – but what’s actually happening in the code? First of all, we’re using useStoryblokApi to fetch the Config story without loading the Storyblok Bridge automatically. You can learn more about the differences between useAsyncStoryblok, useStoryblokApi and useStoryblokBridge in the documentation on Github. What’s important to notice is that an additional parameter – resolve_links – is passed to the apiOptions. This is used to actually get the URLs of the stories that we link internally. You can learn more about this and other parameters in our Content Delivery API docs.

    Next, a reactive object by the name of headerMenu is created and the contents of the header_menu field that we created earlier is stored as its value. This can then be used in the template using a simple for loop.

    Wrapping Up

    Congratulations, you have successfully created a dynamic menu in Storyblok and Nuxt!

    Next Part:

    Continue reading and learn How to Create Custom Components in Storyblok and Nuxt.

    Resource Link
    Storyblok Nuxt 3 Ultimate Tutorial https://www.storyblok.com/tp/storyblok-nuxt-ultimate-tutorial
    Storyblok Nuxt 3 Module https://github.com/storyblok/storyblok-nuxt
    Storyblok Nuxt Technology Hub https://www.storyblok.com/tc/nuxtjs
    Storyblok APIs https://www.storyblok.com/docs/api
    Nuxt 3 https://v3.nuxtjs.org/

    Developer Newsletter

    Want to stay on top of the latest news and updates in Storyblok?
    Subscribe to Code & Bloks - our headless newsletter.

    An error occurred. Please get in touch with marketing@storyblok.com

    Please select at least one option.

    Please enter a valid email address.

    This email address is already registered.

    Please Check Your Email!

    Almost there! To confirm your subscription, please click on the link in the email we’ve just sent you. If you didn’t receive the email check your ’junk folder’ or