Contents

Add a headless CMS to VueJs in 5 minutes

In this short article, we will show you how you can use the API-based CMS Storyblok for components in combination with the progressive JavaScript Framework “Vue.js”. At the end of this article, you will have a Vue.js Application which renders components filled with data from Storyblok.

During this article, for example, we will use following JSON (it is the default setup you receive after creating a new space) which comes from the Storyblok API to render our first story:

{
  "story": {
    "name": "home",
    "content": {
      "component": "page",
      "body": [
        {
          "component": "teaser",
          "headline": "Easy - isn't it?"
        }
      ]
    },
    "slug": "home",
    "full_slug": "home"
  }
}

Vuejs

I’m sure that most of you are already familiar with Vue.js and it’s basics - if not - Here you can find their introduction.

Let’s start with the installation of their command line interface to get the thing running:

npm install -g vue-cli

(Prerequisites: Node.js (>=4.x, 6.x preferred), npm version 3+ and Git.)

Start a new vuejs project

You can add Storyblok to existing projects as well - for simplicity we will show how to add Storyblok to a completely fresh project - so a beginner to the world of Vue.js can use Storyblok as their CMS as well. Execute the following commands so you get a new project ready to start with:

vue init webpack vue-storyblok

cd vue-storyblok && npm install && npm run dev

start a new vuejs project.

Including the Storyblok JavaScript Bridge

You will need to include the Storyblok script in order to use the side by side editor. The script allows you to listen and subscribe to events like “update”, “published”, so you can instantly refresh your page after a change. You can add it directly to your index.html.

<script type="text/javascript" src="//app.storyblok.com/f/storyblok-latest.js"></script>

Install our Vue.js Editable Plugin

The Storyblok Vue.js plugin allows you to make your elements clickable and the integration easier since all that needs to be done would be to add v-editable="blok" to your component and pass the current component object to it.

npm install storyblok-vue --save-dev

Install the storyblok vuejs plugin.

In your /src/main.js, we import the plugin so vuejs will be able to use it.

import StoryblokVue from 'storyblok-vue'

Vue.use(StoryblokVue)

Install our JS client

Final thing to install will be our Universal JavaScript Client it will allow you to easily consume content from our API - you can leave that out if you prefer to use other clients such as axios (our client is based on them), native fetch or anything similar.

npm install storyblok-js-client --save

Load our first story

Let’s open the /src/components/HelloWorld.vue and add an empty story to data.

<script>
export default {
  name: 'HelloWorld',
  data () {
    return {
      msg: 'Welcome to Your Vue.js App',
      story: {
        content: {
          body: []
        }
      }
    }
  }
}
</script>

As you can see this is the basic form according to the JSON at the beginning - the content can be any component you want - defined by yourself. For this example, we prepared a property called body which is an array of sub-components (e.g. teaser).

Next, we will have to initialize the Storyblok API-Client and the window client bridge so you will have access to your space & the connected stories - you can switch the token to one of yours afterwards.

<script>
// 1. Require the Storyblok client
import StoryblokClient from 'storyblok-js-client'

// 2. Set your token
const token = 'akYA0RB4BzCPUoRfjIvUdQtt';

// 3. Initialize the client with the preview token so we can access our API easily
// from your space dashboard at https://app.storyblok.com
let storyapi = new StoryblokClient({
  accessToken: token
})  

export default {
  name: 'HelloWorld',
  data () {
    return {
      msg: 'Welcome to Your Vue.js App',
      story: {
        content: {
          body: []
        }
      }
    }
  },
  created () {
    // 4. Initialize the Storyblok Client Bridge to allow us to subscribe to events
    // from the editor itself.
    window.storyblok.init({
      accessToken: token
    })
    window.storyblok.on('change', () => {
      this.getStory('home', 'draft')
    })
    window.storyblok.pingEditor(() => {
      if (window.storyblok.isInEditor()) {
        this.getStory('home', 'draft')
      } else {
        this.getStory('home', 'published')
      }
    })
  },
}
</script>

You may notice the this.getStory method in our created block. This method doesn’t exist yet. So let’s add the method to our Vue.js methods. You can see that we’re using the Storyblok API client get function to load a story according to the slug passed to it. You can use all query parameters which can be found in our docs, if you want to load multiple stories that are in your articles folder you can use the starts_with instead of the slug parameter.

methods: {
  getStory(slug, version) {
    storyapi.get('cdn/stories/' + slug, {
      version: version
    })
    .then((response) => {
      this.story = response.data.story
    })
    .catch((error) => {
      console.log(error);
    })
  }
}

You can exchange the storyapi.get function with any other HTTP GET request module like: axios, fetch or even a simple xhr request. Our Storyblok JS API client does use axios under the hood btw. Make sure to attach your token to the request as an URL param, otherwise you won’t be able to load your content.

Good Job!

You’ve loaded your first story - now we only have to display the data we already received. Let’s change the template of the HelloWorld.vue to something more generic than a list of links:

<template>
  <div>
    <component :blok="story.content" :is="story.content.component"></component>
  </div>
</template>

<script>
// 1. Require the Storyblok client
import StoryblokClient from 'storyblok-js-client'

// 2. Set your token
const token = 'akYA0RB4BzCPUoRfjIvUdQtt';

// 3. Initialize the client with the preview token so we can access our API easily
// from your space dashboard at https://app.storyblok.com
let storyapi = new StoryblokClient({
  accessToken: token
})  

export default {
  name: 'HelloWorld',
  data () {
    return {
      msg: 'Welcome to Your Vue.js App',
      story: {
        content: {
          body: []
        }
      }
    }
  },
  created () {
    // 4. Initialize the Storyblok Client Bridge to allow us to subscribe to events
    // from the editor itself.
    window.storyblok.init({
      accessToken: token
    })
    window.storyblok.on('change', () => {
      this.getStory('home', 'draft')
    })
    window.storyblok.pingEditor(() => {
      if (window.storyblok.isInEditor()) {
        // draft will be loaded if embeded in app.storyblok.com
        this.getStory('home', 'draft')
      } else {
        // published versions will be loaded outside of app.storyblok.com
        // so make sure to published your entries - otherwise this will be a 404
        this.getStory('home', 'published')
      }
    })
  },
  methods: {
    getStory(slug, version) {
      storyapi.get('cdn/stories/' + slug, {
        version: version
      })
      .then((response) => {
        this.story = response.data.story
      })
      .catch((error) => {
        console.log(error);
      })
    }
  }
}
</script>

This will include a component according to the components added in the Story we already loaded. Vue.js does offer a generic way to include components by it’s name such dynamic components are a great way to build generic components that can be composed into all kind of pages using Storyblok. You will end up with a white page after this, since we do not have any components to include yet. Storyblok already offers the content for some components and has defined a set of “default” content structures for components like teaser, grid, column - we’re about to creat them below.

Now let’s create our first component

In the demo content you receive during the on-boarding later, you will see that the first component, a content-type, is the page component (you can name components however you want.) only has one array as property - body.

"body": [
  {
    "headline": "Easy - isn't it?",
    "component": "teaser"
  }
]

All we need to do now is to iterate through all the components which are nested in the array.

We need to create a /components/Page.vue file in our project which would look like the following to achieve that:

<template>
  <div v-editable="blok">
  <template v-for="item in blok.body">
    <component :key="item._uid" :blok="item" :is="item.component"></component>
  </template>
  </div>
</template>

<script>
export default {
  props: ['blok']
}
</script>

You can see that in each iteration the property :blok="item" will be passed to the child component containing all fields defined in Storyblok and therefore also all nested components.

Don’t forget to register the new component in your /src/main.js so Vue knows about it.

import Page from '@/components/Page'

Vue.component('page', Page)

Our second Component

The next component we need to add to finally display something is the teaser component. Let’s go ahead and create the /components/Teaser.vue file with the following content

<template>
  <div class="teaser">
    <!--
    The _editable attribute makes the next
    DOM-element clickable so the side by side editor can
    show the right component.
    -->
    <div v-editable="blok" class="teaser__inner">
      <h1>
        <!--
        You can access every attribute you
        define in the schema in the blok variable
        -->
        {{blok.headline}}
        </h1>
        <h2>
          You can create new components like this - to create your own set of components.
      </h2>
    </div>
  </div>
</template>

<script>
export default {
  props: ['blok']
}
</script>

and again register it in your /src/main.js:

import Teaser from '@/components/Teaser'

Vue.component('teaser', Teaser)

Congrats!

You’ve added Storyblok to VueJs.

Storyblok with vuejs - easy - isnt it?

Creating a Storyblok Space

Now that you have successfully integrated Storyblok in your project let’s create a “Story” in your own Storyblok space if you did not do that already.

  1. Go to https://app.storyblok.com/#!/signup and do the signup
  2. Follow the guide to create a new empty space.

Exchange the preview token

Copy your preview token to receive the draft version of your content. The preview token is a read only token. If you don’t want that the draft version of your content will be visible for the public you need to protect it via a server side client or publish two versions - one with the public token for production and one with the preview token to preview changes. Insert your token in /components/HelloWorld.vue.

...
// 2. Set your token
const token = 'akYA0RB4BzCPUoRfjIvUdQtt';
...

Onboarding

Add your environment

After adding your own token from your Space to your project, which you can find in the code examples of the on-boarding - we will have to also tell Storyblok where to find our dev environment. For this you can add the following URL directly in the last step of the on-boarding localhost:8080 as a new environment.

Editing with Storyblok

Well Done!

Try out to click on your components and insert a text and click “Save”. Your component should now be updated with the new content. Next step is to repeat the steps above to configure more components. You can also create grid and column components which can nest other components with the schema type “components”. A root component is such a component which has other components nested. If you’re into Vue and liked this tutorial - check out our Complete Guide to Build a Full Blown Multilanguage Website with Vue & Nuxt.js


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.