Almost EVERYONE who tried headless systems said they saw benefits. Download the state of CMS now!

Storyblok now on AWS Marketplace: Read more

O’Reilly Report: Decoupled Applications and Composable Web Architectures - Download Now

Empower your teams & get a 582% ROI: See Storyblok's CMS in action

Skip to main content

Create Dynamic Menus in Storyblok and Laravel

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.

GitHub source code:

If you’re in a hurry, have a look at our source code demo in GitHub!

Section titled Requirements Requirements

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

Section titled Setup in Storyblok 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}.


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}.

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}.

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}.

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}.

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}.

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 of our Storyblok space. In here, we want to create a new story with the name Config {2}, using our recently created content type Config {3}.

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.

Dynamic menu rendered correctly in Laravel

Dynamic menu rendered correctly in Laravel

Section titled Rendering the Menu in Laravel Rendering the Menu in Laravel

Having taken care of our setup in Storyblok, we can now turn to the code and implement our dynamic menu in the blade template. To accomplish that, we have to refactor a bit of the code we already implemented for the static header, and add the dynamic Header component. In detail we will:

  • add a new component class
  • add the logic for retrieving the new config Story (via the Storyblok PHP SDK) in a new component class
  • refactor the component blade template and make it dynamic

Section titled The Header component class The Header component class

We are going to create a component class for implementing the logic for retrieving the config Story from Storyblok. Create a new file app/View/Components/Header.php. We will implement the two methods: __construct() and render():

app/View/Components/Header.php
        
      <?php

namespace App\View\Components;

use Illuminate\Support\Arr;
use Illuminate\View\Component;

class Header extends Component
{
    public $headerMenu;

    /**
     * Create a new component instance.
     *
     * @return void
     */
    public function __construct()
    {
        $storyblok = new \Storyblok\Client(config('storyblok.api_key'));
        $storyblok->editMode();
        $storyblok->resolveLinks('url');
        // retrieving "config" Story
        $response = $storyblok->getStoryBySlug('config');
        $data = $response->getBody();
        $this->headerMenu = Arr::get($data, 'story.content.header_menu', []);
    }

    /**
     * Get the view / contents that represent the component.
     *
     * @return \Illuminate\View\View|\Closure|string
     */
    public function render()
    {
        return view('components.common.header');
    }
}

    

In the constructor method we are going to create an instance of Storyblok Client, using the API key from the configuration, setting the editMode (for retrieving the draft version), and setting the resolve links for the URL. For resolving the links we are using: $storyblok->resolveLinks('url');.

If your space was created in the United States, don't forget to set the apiRegion parameter in Storyblok\Client initialization. If you need more examples, you can see the PHP SDK readme documentation for "Initialization for US spaces"

In the constructor we are retrieving the information from a specific Story ('config'): $response = $storyblok->getStoryBySlug('config');.

In the render() method we are going to load the blade in resources/views/components/common/header.blade.php via the view() helper: return view('components.common.header');

Section titled The Header blade template The Header blade template

In the file resources/views/components/common/header.blade.php already created in the Part 2 of the Laravel Ultimate Tutorial series, we can load dynamically the menu items thanks to the work made in the component class that filled the $headerMenu variable. The $headerMenu variable is an array, where we can find the menu items, and for each menu item, we can access the full_slug property for composing the destination URL and the name for the label of the link.

resources/views/components/common/header.blade.php
        
      <header class="w-full h-24 bg-[#f7f6fd]">
  <div class="container h-full mx-auto flex items-center justify-between">
    <a href="{{url('/')}}" <h1 class="text-[#50b0ae] text-3xl font-bold">Storyblok &#10084; Laravel</h1>
    </a>
    <nav>
      <ul class="flex space-x-8 text-lg font-bold">
        @foreach ($headerMenu as $menuItem)
        <li>
          <x-common.menu_item link="{{url(
            Arr::get($menuItem, 'link.story.full_slug', '/')
          )}}" label="{{Arr::get($menuItem, 'link.story.name', '')}}" />
        </li>
        @endforeach
      </ul>
    </nav>
  </div>
</header>
    

Now, we can refactor the way we are calling the Header component from the blade templates. Let's remove the header in the layout file:

resources/views/components/common/layout.blade.php
        
      <body class="antialiased">
-  <x-common.header></x-common.header>
  {{ $slot }}
  <script type="text/javascript" src="//app.storyblok.com/f/storyblok-v2-latest.js"></script>
  <script type="text/javascript">
    

Afterwards, we can include the new header here:

resources/views/components/page.blade.php
        
      +<x-header></x-header>
{!! Arr::get($blok, '_editable', '') !!}
<div class="font-[Roboto] px-4">
    @foreach ($blok['body'] as $blok)
        <x-dynamic-component :component="$blok['component']" :blok="$blok" class="mt-4" />
    @endforeach
</div>
    

And finally create the resources/views/components/config.blade.php file, for allowing the correct rendering when the editor user will select the config Story in the visual editor:

resources/views/components/config.blade.php
        
      <x-header></x-header>
    

When opening the visual editor, you can now see the expected result:

Your Laravel application renders the dynamic menu

Your Laravel application renders the dynamic menu

Please remember to start your Laravel application locally via php artisan server. Since HTTPS is a requirement for the Visual Editor, you also have to use the following command (as explained in Part 1):

        
      local-ssl-proxy --hostname 127.0.0.1 --source 8010 --target 8000 --cert localhost.pem --key localhost-key.pem
    
5 min tutorial:

If you missed something, you can start reading and learn Add a headless CMS to Laravel in 5 minutes.

Author

Roberto Butti

Roberto Butti

Roberto is a Developer Relations Engineer at Storyblok who loves supporting teams for building websites focused on the code's performance and code quality. His favorite technologies include Laravel, PHP, Vue, Svelte and ... Storyblok. He loves exploring and designing composable architectures.