How to add Storyblok CMS to Vue Storefront 2
Tags: vue, nuxt, cms, javascript
Published: 2022-05-18 09:47:02

At first glance, having a Content Management System might not be that important, but I can assure you that when your E-Commerce system will start to grow, having CMS will prove to be very useful. I have been working with several CMS's already and I had the best experience with Storyblok and wanted to show you how to connect it to Vue Storefront project.

Below you can see a video of my talk at Vue Storefront Conference about building an E-Commerce with Vue Storefront, Shopify and Storyblok:

{% youtube RSSbC5HBdBw %}

Below is a step by step video tutorial on how to add Storyblok Headless CMS to Vue Storefront 2 project with Shopify E-Commerce Platform:

{% youtube ndM9uoQW6xE %}

*No worries, the integration with Storyblok works the same way for other E-Commerce integrations like Magento, Vendure and many more. The process is exactly the same. I selected Shopify due to easiness of setting up.*

## Code

First of all, let's install the `vue-storefront/storyblok` and `storyblok-vue` packages:

```bash
yarn add @vue-storefront/storyblok storyblok-vue # npm install @vue-storefront/storyblok @storyblok/vue
```

Next, let's create a `cms` folder inside a root directory:

```bash
mkdir cms
```

And copy a special `RenderContent.vue` component from `@vue-storefront/storyblok` package:

```bash
cp node_modules/@vue-storefront/storyblok/components/RenderContent.vue cms/
```

This component will be used to render the content we will get from the Storyblok into our application.

Now, we will create a dynamic `Banner.vue` component based on Storefront UI `SfBanner.vue` component. If you haven't tried the Storefront UI library yet you can do so [here](https://docs.storefrontui.io/). Keep in mind that the names of properties in Storyblok must be the same as the names of your props. I am explaining it in a video.

```vue
<template>
  <SfBanner
    :title="title"
    :subtitle="subtitle"
    :description="description"
    :button-text="banner_text"
    :link="banner_link"
    :image="image.url"
  />
</template>

<script lang="ts">
import Vue from 'vue'
import { SfBanner } from '@storefront-ui/vue';

export default Vue.extend({
  name: 'Banner',
  components: {
    SfBanner
  },
  props: {
    title: {},
    subtitle: {},
    description: {},
    banner_text: {},
    banner_link: {},
    image: {
      type: Object,
      required: true
    },
    background: {}
  }
})
</script>
```

The next step right now is to create a `cms.js` plugin inside of `plugins` directory:

```js
import Vue from 'vue'
import Banner from '~/cms/Banner.vue'

Vue.component('banner', Banner)
```

In order for the integration to work correctly we have to register it in the `middleware.config.js` file:

```js
module.exports = {
  integrations: {
    shopify: { ... }
    },
    sb: {
      location: '@vue-storefront/storyblok/server',
      configuration: {
        token: process.env.STORYBLOK_TOKEN,
        cacheProvider: 'memory',
      },
    }
  }
};
```

After that, we need to register few things in the `nuxt.config.js` file for the integration to work as expected:

```js
...
  plugins: [
    '~/plugins/scrollToTop.client.js',
    '~/plugins/cms' // <-
  ],
...
buildModules: [
    // to core
    '@nuxtjs/composition-api/module',
    '@nuxtjs/pwa',
    '@nuxt/typescript-build',
    '@nuxtjs/style-resources',
    '@nuxtjs/device',
    [
      '@vue-storefront/nuxt',
      {
        useRawSource: {
          dev: ['@vue-storefront/shopify', '@vue-storefront/core', '@vue-storefront/storyblok'], // <-
          prod: ['@vue-storefront/shopify', '@vue-storefront/core', '@vue-storefront/storyblok'] // <--
        }
      }
    ],
    ['@vue-storefront/nuxt-theme'],
    [
      '@vue-storefront/shopify/nuxt',
      {
        i18n: {
          useNuxtI18nConfig: true
        }
      }
    ]
  ],
...
  modules: [
    '@vue-storefront/storyblok/nuxt', // <-
    'nuxt-i18n',
    'cookie-universal-nuxt',
    'vue-scrollto/nuxt',
    '@vue-storefront/middleware/nuxt'
  ],
...
```

Finally, don't forget to add an environment variable to `.env` with your Storyblok token:

```bash
STORYBLOK_TOKEN=<YOUR_STORBLOK_TOKEN>
```

## Storyblok

In this part, we will switch from the code into Storyblok in order to create content in CMS and configure it to work with Vue Storefront

We will create a new space for our project:

![Storyblok Space](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a3zoocc010o5k62704st.png)

Now, let's move into components section and create a `banner` component:

![Storyblok Component](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c7ravlhrxhj4uy3kgvpb.png)
 
Next, in the content section copy the Storyblok token and put it into your application (unless you did that already)

![Storyblok Content](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9jxb2k6gkkzz2npyemn8.png)

To make the Storyblok preview work as expected, add location in the configuration:

![Storyblok preview](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oze7qz7cqa9ez3b4augu.png)

Finally, in the content configuration, add a real path:

![Storyblok Content Configuration](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wu98nuglb1t2nm29p7kv.png)

If we have configured everything correctly, we should see our application inside Storyblok preview:

![Storyblok VSF Preview](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d80qsyi87f9l2eq8ubza.png)
 

## Code part two

Now that we have Storyblok configured, let's jump in once again to the code to add dynamic content to our application.

In the `Home.vue` page, let's add few things to allow dynamic content. First of all add below line somewhere in your template tag:

```vue
<template>
...
  <render-content :content="body" />
...
</template>
```

Next, in the script section let's import the required components and composables:

```vue
<script>
import { useContent } from '@vue-storefront/storyblok'
import RenderContent from '~/cms/RenderContent.vue'
...

export default {
  ...
  components: {
    ...
    RenderContent
  },
  setup() {
    ...
    const { search, content } = useContent('home')
    const body = computed(() => content.value.body)

    onSSR(async () => {
      ...
      await search({ url: `home?cv=${Math.floor(Date.now()/1000)}` })
    });

    return {
      ...
      body
    };
  }
}
</script>
```

## Storyblok part two

The final step is to add a new content for banner component inside Storyblok:

![Storyblok Content Component](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4kalaqkayzl983mtefdp.png)

If we did everything correctly, we should see the following result in both Storyblok preview and our application:

![New Storyblok Content](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j76nroqxe66vpgvoxj3m.png)
 
## Summary

Well done! You have just integrated Storyblok to your Vue Storefront 2 online store. This is just the beginning of the integration and overall content creation but it should be a solid start for building content driven E-Commerce.

## Bonus

- [Vue Storefront, Shopify, Storyblok repository](https://github.com/Baroshem/vsf-shopify-storyblok-demo)
- [Samuel Snopko video about Storyblok and Vue Storefront](https://www.youtube.com/watch?v=CwUVtMMUeXs)
- [Vue Storefront docs about Storyblok integration](https://docs.vuestorefront.io/storyblok/)