How to build a website header menu navigation with Storyblok

In this tutorial you will learn how to setup your project to load global stories to render a header menu and footer content - it is part of the Storyblok CMS kickstart series so you may have a look at the other tutorials as well. You can use the same configuration in Storyblok for your own setup - to simplify the tutorial we’re using the rendering service and liquid.

What we will do:

  1. Setting up a “global” component as root for a new story.
  2. Setting up the template for the global story.
  3. Loading the content of the global resource wherever we need.
  4. Render a header navbar using those data and display it using bootstraps navbar.

Setting up a “global” component

The global component is just another component - the only difference is that the flag for “using as root” is checked and the “can be nested” checkbox is not. Using the configuration like that will allow us to use it as it’s own page type. Since each page in this tutorial should have a header menu and we don’t want to add such a component every time we’re creating a new story with that component as root - the idea of a global resource is the way to solve that.

a global component

After creating the new component navigate to “content” - press “Add” and select the “global” component as the main component in the select in that popup. This should look like in the picture below.

Selection of the global root component

Setting up the template for the global story

The slug of this story can be named however you want - we’re going for global since we will add all global content resources in here. You will see that in the live preview we receive an error that global.liquid can not be found. The _global.liquid is only the place we will store data - it will not be the actual rendered component after the tutorial. We will use it to render a section telling the user that he can edit global resources in this place.

  <h1>Global Resources / Settings</h1>
  <p>Click here to edit header and footer content</p>

After this, we can add new schema keys to the global component. Let us start with some header links. Press “add Schema key” and insert a new field header_links of the field type components. Click on “add components” and create the next new component header_link.

Header link added to global

Click on your newly created component and lets add 2 new fields - the actual link (link - type: Weblink/Storylink) and a display name (display_name - type: text).

header link content

That’s the whole setup to store data for your global content already. in the next step we will have a look on how to load that content and render it properly. You can add a value for the link (just select home) and enter a display name - you can also add more header links if you want by simply pressing “add component” in the header_links field and select the header_link.

Loading the content of the global resource

Using the rendering service we can use the build in functionality for loading stories using their slug - if you’re using one of our SDKs you also can directly access such functionality. However, if you’re using your own setup and access the API directly you can still get a story by slug and simply pass that data to your template.

We’re using the following line in the page.liquid to load the global stories data - you can add it above the <main> tag:

<!-- Sets the story with the slug 'global' to the variable global: -->
{% set global from story id:'global' %}

That’s it - the variable “global” now contains all the information we have added during the setup.

Render a header navigation using the data

As we did before in the grid example we will start with including some styles. I’ve used the Bootstrap 3.3 configurator to only include navbar stylings. Lets start with creating the file: /source/scss/above/_navbar.scss with the following content:

@import url('');

The gulp process should update your stylings and you should be ready to go. Next thing on our to do is the actual HTML - for this, we will introduce a new component in our template. Let’s create the file /views/components/_navbar.liquid with the following content:

<nav class="navbar navbar-default">
  <div class="container-fluid"> 
    <ul class="nav navbar-nav">
      <li class="active">
        <a href="#">Home</a>
        <a href="#">Link</a>
        <a href="#">Link</a>

Since this navbar should be rendered on every page we will have to include it globally. To do so we go back to the page.liquid and add the following line above the <main> tag but below the command for loading the global data.

<!-- Include the global navigation bar -->
{% include 'navbar' %}

This is a basic liquid functionality - if you’re using a templating engine we should also be able to include snippets and passing variables. After saving you should now see the following content on your screen:

Hardcoded Navbar result

Next step will be to change the hard coded links to be generated links according to the header_links we’ve added in the global story. To achieve this we will have to iterate through the header_link components of the global data. We can access this variable here because we’ve loaded it before and liquid directly passes that variable in all included components. For your own setup make sure the variable is available in your navbar component as well.

<nav class="navbar navbar-default">
  <div class="container-fluid"> 
    <ul class="nav navbar-nav">
      {% for header_link in global.content.header_links %}
        <a href="{{|url }}">{{ header_link.display_name }}</a>
      {% endfor %}

After replacing your current navbar with the code above you should already see that the navigation items have changed to the elements you’ve added in the header_links field. You will also see that we’ve used the url filter {{|url }}. This filter will directly look up the referenced story-id using the Links API and return the correct slug of the linked story - this needs to be done like that because a slug can be changed - the internal id will not. So if you’re using VueJS, PHP or Python - match the ID of your link object to the ID in the result of the Links API - the url property in the object will be filled if you’ve entered an external link and no internal reference.

Dynamically rendered navbar

What did you just learn?

  • You’ve created a new page type/root component
  • You access the content of another story on every story.
  • You’ve rendered a global component without adding it as an actual component (navbar)
  • You’ve created content only components (header_link) without template
  • You’ve used the Links API to get the full slug of a story by its ID

Other examples for that use-case:

  • Footer
  • Sidebars
  • basically component on every story with the same content

I hope you enjoyed this tutorial and I would be happy to receive feedback! You can find the source code using the release list on Github: Kickstart - Releases and the hosted final result on Storyblok.

More to read...

About the author

Dominik Angerer

Dominik Angerer

A web performance specialist and perfectionist. After working for big agencies as a full stack developer he founded Storyblok. He is also an active contributor to the open source community and one of the organizers of Stahlstadt.js.