Create a dashboard with TailwindCSS - Adding Storyblok
Tags: storyblok, vue, nuxt, tutorial
Published: 2020-05-17 19:03:48

{% tweet 1262093895204311040 %}

## Prerequisites

As this post is the continuation of a series, if you haven't read it yet you should take a look at  [Part 1](https://www.dawntraoz.com/blog/create-a-dashboard-with-tailwindcss-part-1) and [Part 2](https://www.dawntraoz.com/blog/create-a-dashboard-with-tailwindcss-part-2) before starting.

Also, If you already have an account at Storyblok, [log in](https://app.storyblok.com/), and if not [register](https://app.storyblok.com/#!/signup) to start. 

Now we're ready to follow!

---

Maybe you're wondering why I didn't add Storyblok in the first place. Because now I can show you how easy it can be to adapt your project to this CMS, just like I did with my personal website.

> If you want to see how to add it from scratch, I recommend you to read [Add a headless CMS to Nuxtjs](https://www.storyblok.com/tp/headless-cms-nuxtjs)

## Step 1. Create a Storyblok space

It's as simple as pressing the button **Create new space** at the sidebar and add on the input field the name we have chosen for our web app:

![Create new space at Storyblok](//a.storyblok.com/f/79165/848x560/52217d9ae9/create-space.png)

Once you have created it, you will see a page called *home* with sample content. It's time to let our project connect with this data!

## Step 2. Connect to the Storyblok API

The fascinating thing about all this, is that they already give us everything ready. If you use any modern technology other than NuxtJS, you will have a Storyblok client ready, and for Nuxt it’s as simple as add their module, we just have to install it,

```bash
npm install storyblok-nuxt --save
```

and configure the module in nuxt.config.js with the **accessToken**, found in *Settings > API-Keys* in the panel. Just add:

```javascript
modules: [
    ['storyblok-nuxt', {
      accessToken: 'YOUR_ACCESS_TOKEN',
      cacheProvider: 'memory'
    }]
]
```

Now we can make requests to our content through **this.$storyapi** 🎉

## Step 3. Making our content dynamic

Let's start at the beginning.

So far we have created the page with the style guide, but also, we have a home page and at some point we will want to extend the number of pages. So we're going to create a dynamic path for our pages.

In **NuxtJS** it's as simple as naming our file with a slash and the field that will generate that path, in this case **_slug.vue**.

We added in **_slug.vue** the following template:

```html
<template>
  <div>
    <header>
      <h1>{{ story.name }}</h1>
    </header>
  </div>
</template>
```

and the script with the API call from the [asyncData](https://nuxtjs.org/guide/async-data/) method:

```javascript
<script>
export default {
  asyncData(context) {
    // home slug content by default
    const slug = context.params.slug ? context.params.slug : 'home'

    // Load the JSON from the API
    return context.app.$storyapi
      .get(`cdn/stories/${slug}`, {
        version:
          context.query._storyblok || context.isDev ? 'draft' : 'published'
      })
      .then((res) => {
        return res.data
      })
      .catch((res) => {
        context.error({
          statusCode: res.response.status,
          message: res.response.data
        })
      })
  },
  data() {
    return { story: { content: {} } }
  }
}
</script>
```

Changing the name of our Home page in the Storyblok panel, by Cabbage we see the same result as our previous **index.vue**, we can now remove it.

![Homepage](//a.storyblok.com/f/79165/1229x744/3364a899a8/homepage.png)

> To see the result in the Storyblok panel preview we must add our local route, in my case http://localhost:3000/, go to Settings > General and add it in **Location (default environment)**.

---

**Add dynamic components**

In order to represent the blocks and content types that the Storyblok API will return, we must use [the dynamic components of VueJS](https://vuejs.org/v2/guide/components.html#Dynamic-Components).

So let’s add in our template *_slug.vue* the following code:

```html
<template>
  <div>
    <header>
      <h1>{{ story.name }}</h1>
    </header>
    <!-- Component: Content Type -->
    <component
      :is="story.content.component"
      v-if="story.content.component"
      :key="story.content._uid"
      :blok="story.content"
    ></component>
  </div>
</template>
```

Now we simply have to globally create the components that we'll define in our Storyblok space. For now, we only have one Content Type called page, let's see how we'll do it!

First, we will create a plugin to add the new components, in the **plugins** folder add the file **components.js** with the content below,

```javascript
import Vue from 'vue'
import Page from '@/components/Page.vue'

Vue.component('page', Page)
```

and now, call it in the **nuxt.config.js** as:

```javascript
plugins: ['~/plugins/components.js']
```

Then, we can create our first *Content Type* in the components folder as **Page.vue**, we'll have another **dynamic component** that will represent the blocks we add in the body field (*blok.body*):

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

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

**Make the Styleguide page dynamic**

We will add a new Entry, a new page, to our Storyblok panel with the following name and slug:

![New Entry](//a.storyblok.com/f/79165/395x304/732898d3ef/new-entry.png)

---

Now go to the **Components** option in the panel's sidebar and delete all the default components except the *Content Type* **Page**.

Let's create our own!

- Click on **New** and choose the option **Nestable**, add the name of the component with lowercase and dashes.

  ![New component](//a.storyblok.com/f/79165/465x222/ca16645843/new-component.png)

- Define the **schema** with the **fields** you will need on this component and choose their **type**.

  ![Component schema](//a.storyblok.com/f/79165/464x464/89cb515739/component-schema.png)

- Then, create your own **Vue component** to represent this component, or reuse one you have already created as I did.

  First, I have in **StyleSection.vue**:

  ```html
  <template>
    <div class="pt-8">
      <header class="pb-6">
        <h3 class="capitalize">
          <slot name="title"></slot>
        </h3>
        <p class="pb-2 border-b border-gray-300 text-gray-700">
          <slot name="title"></slot> for app theme.
        </p>
      </header>
      <slot />
    </div>
  </template>
  ```
  
  Now I change the slots by the blok content that comes in props:

  ```html
  <template>
    <div class="pt-8">
      <header class="pb-6">
        <h3 class="capitalize">
          {{ blok.title }}
        </h3>
        <p class="pb-2 border-b border-gray-300 text-gray-700">
          {{ blok.title }} for app theme.
        </p>
      </header>
      <component
        :is="blok.component"
        v-for="blok in blok.body"
        :key="blok._uid"
        :blok="blok"
      ></component>
    </div>
  </template>
  ```

  Once I’d updated the component I added it to the **component.js** file as:

  ```javascript
  ...
  import StyleSection from '@/components/StyleSection.vue'

  ...
  Vue.component('style-section', StyleSection)
  ```

- Use it on your page pressing **Add block** button and selecting your new component:

  ![Adding new components](//a.storyblok.com/f/79165/1229x744/6b526863dc/adding-new-component.png)

---

Now it would be time to do the same with the components we have left: **Colors**, **Gradients**, **Fonts** and **Icons**.

We can choose to dynamize the whole content, adding all the fields that we want to represent in each component or, as it is my case, create a **Nestable** component without schema and simply add the component in StyleSection **Add block**.

> Why have I done this? Because as it is a style guide its content will be static and, besides, it uses the TailwindCSS classes which, being in the *.vue* file itself, PurgeCSS does not remove them from our CSS file.

Then we will have something like this in our panel:

![Img](//a.storyblok.com/f/79165/794x377/fe236b8b29/list-components.png)

And the equivalent in our **components.js** file:

```javascript
import Vue from 'vue'
import Page from '@/components/Page.vue'
import StyleSection from '@/components/StyleSection.vue'

/* Styleguide components */
import Colors from '@/components/styleguide/Colors.vue'
import Gradients from '@/components/styleguide/Gradients.vue'
import Fonts from '@/components/styleguide/Fonts.vue'
import Icons from '@/components/styleguide/Icons.vue'

Vue.component('page', Page)
Vue.component('style-section', StyleSection)

/* Styleguide components */
Vue.component('colors', Colors)
Vue.component('gradients', Gradients)
Vue.component('fonts', Fonts)
Vue.component('icons', Icons)
```

What's great? From now on, everything we create can have its storage place and we can edit and duplicate it easily!

In the following articles we will see the creation of charts for the dashboard bringing them to life with TailwindCSS and we will continue to energize it with Storyblok 😍

I hope Storyblok makes your life as easy as it is for me 💞

As always, I leave the repository here in case you want to take a look to progress: [https://github.com/Dawntraoz/cabbage](https://github.com/Dawntraoz/cabbage)