⚠️Draft Content

Storyblok Raises $80M Series C - Read News

What’s the True Total Price of Enterprise CMS? Find out here.

Skip to main content

Add a headless CMS to Nuxt in 5 minutes

Try Storyblok

Storyblok is the first headless CMS that works for developers & marketers alike.

In this quick walkthrough, we will have a look at how we can use data from the Storyblok API with a Nuxt project to create a website. At the end of this article, you will have a Nuxt project which renders components filled with data from Storyblok.

LIVE DEMO:

If you’re in a hurry, have a look at our live demo in Stackblitz!

Environment Setup

Requirements

To follow this tutorial make sure to meet these requirements:

  • Basic understanding of Vue.js, Nuxt.js and Javascript
  • Node.js LTS version
  • An account in the Storyblok App

Create a Nuxt.js project

Since Nuxt 3 is still in release candidate stage, let’s use Nuxt 2 for this tutorial.

Following the Nuxt.js official installation guide, we can easily create our project using the installation tool create-nuxt-app. Use it by running the following command:

        
      npm init nuxt-app <project-name>
    

Let’s choose the following options:

  • Programming language: JavaScript
  • Package manager: npm
  • UI framework: Tailwind CSS
  • Nuxt.js modules: none
  • Linting tools: none
  • Testing framework: none
  • Rendering mode: Universal (SSR/SSG)
  • Deployment target: Static (Static/Jamstack Hosting)
  • Development tools: jsconfig.json

Once you installed the dependencies and run npm run dev in the project folder, you’ll see this screen when you open http://localhost:3000 in your browser:

Welcome screen of your Nuxt project

Welcome screen of your Nuxt project

Configuration of the space

You can easily configure a new space by clicking Add Space {1} after having logged in to Storyblok.

Creating a new space in Storyblok
1

Creating a new space in Storyblok

Create a new space in the Storyblok app by choosing the Create space {1} option. Pick a name for it {2}. Optionally, you can choose between different server locations for your space {3} (if you choose the United States or China, please be mindful of the required API parameter explained hereinafter).

Creating a new space in Storyblok
1
2
3

Creating a new space in Storyblok

Shortly afterward, a Storyblok space with sample content has been created for you. Let’s open the Home story by first clicking on Content {1} and then on Home {2}:

Opening the Home story
1
2

Opening the Home story

Now you’ll see the default screen and the Visual Editor:

Visual Editor representing your Home story

Visual Editor representing your Home story

Enabling the Visual Editor

In order to actually see your Nuxt project in the Visual Editor, we’ll have to define the default environment URL. Let’s do that by going to Settings > Visual Editor {1} and setting the Location field to https://localhost:3010/ {2}:

Defining the default environment URL
1
2

Defining the default environment URL

hint:

Storyblok v2 requires that your website is served via HTTPS. You can follow these instructions to set up your dev server accordingly.

Now, if you go back to the Home story, you won’t see your Nuxt app in there just yet. Just one more quick step to take: Open the Entry configuration {1} and set the Real path to / {2}. After having saved, you should now be seeing your Nuxt app in the Visual Editor:

Overriding the real path of the Home story
1
2

Overriding the real path of the Home story

Connecting Nuxt to Storyblok

First of all, let’s install our official SDK for Nuxt 2:

        
      npm install @storyblok/nuxt-2
    

This SDK allows you to interact with the Storyblok API. On top of that, it also provides an ingeniously simple way to enable real-time editing! Let’s start configuring it.

Before we jump into the code, we quickly need to grab our API token from our space. Let’s do that by going to Settings > Access Tokens {1} and copying the Preview Token {2}.

Where to get the preview access token of your Storyblok space
1
2

Where to get the preview access token of your Storyblok space

Now we can use this token by adding the following configuration to the buildModules of nuxt.config.js:

        
      {
  buildModules: [
    // ...
    ['@storyblok/nuxt-2/module', { accessToken: '<your-access-token-here>' }],
  ],
}
    

Setting the correct region

Depending on whether your space was created in the EU, the US, Australia, Canada, or China, you may need to set the region parameter of the API accordingly:

  • eu (default): For spaces created in the EU
  • us: For spaces created in the US
  • ap: For spaces created in Australia
  • ca: For spaces created in Canada
  • cn: For spaces created in China

Here's an example for a space created in the US:

        
      apiOptions: {
  region: "us",
},
    
WARN:

Note: For spaces created in any region other than the EU, the region parameter must be specified.

Rendering Dynamic Components in the Nuxt App

The core idea of using Storyblok for this particular use case is the following:

  • Content managers (even if it’s only yourself) can create pages (or stories) composed of different components (or bloks)
  • Developers receive the page in the JSON format by using the Storyblok API and can render components accordingly (this is what we want to accomplish in our Nuxt app)

When you create a new space from scratch, Storyblok automatically creates four default components for you:

  • page (content type)
  • grid (nestable component)
  • feature (nestable component)
  • teaser (nestable component)

You can find find all of these in the Components section of your space.

hint:

Understand the difference between the nestable components and content type in our Structures of Content tutorial.

Creating the Vue Components

Let’s create the counterparts of the four components discussed above in our Nuxt app. To do that, generate the following files in the components/storyblok folder:

components/storyblok/Page.vue
        
      <template>
  <div v-editable="blok">
    <StoryblokComponent
      v-for="blok in blok.body"
      :key="blok._uid"
      :blok="blok"
    />
  </div>
</template>

<script setup>
defineProps({ blok: Object })
</script>
    
components/storyblok/Grid.vue
        
      <template>
  <div
    v-editable="blok"
    class="container mx-auto grid grid-cols-3 gap-12 place-items-center py-16"
  >
    <StoryblokComponent
      v-for="blok in blok.columns"
      :key="blok._uid"
      :blok="blok"
    />
  </div>
</template>

<script setup>
defineProps({ blok: Object })
</script>
    
components/storyblok/Feature.vue
        
      <template>
  <div v-editable="blok">
    <h1 class="text-xl">{{ blok.name }}</h1>
  </div>
</template>

<script setup>
defineProps({ blok: Object })
</script>
    
components/storyblok/Teaser.vue
        
      <template>
  <div v-editable="blok" class="py-16 text-5xl font-bold text-center">
    {{ blok.headline }}
  </div>
</template>

<script setup>
defineProps({ blok: Object })
</script>
    

You may be wondering why we added those components to a storyblok subfolder. By doing that, they’re found and loaded automatically by the Storyblok Nuxt module.

Additionally, you may have noticed that that the Composition API is being used in these components. Since Nuxt 2 is based on Vue 2, it does not provide the Composition API by default. However, we can easily fix that by installing it with the following command:

        
      npm install @nuxtjs/composition-api --save
    

Once it is installed, enable it by adding it to the buildModules of nuxt.config.js:

        
      {
  buildModules: [
     // ...
    '@nuxtjs/composition-api/module'
  ]
}
    

Load content using the API

Now that our components are ready, we can fetch the Home story data. You can view the JSON structure of any story by clicking the Draft JSON {1} button:

Getting the Draft JSON from the Visual Editor
1

Getting the Draft JSON from the Visual Editor

All we have to do is to replace the content of your pages/index.vue with the following code:

        
      <script setup>
  import { useStoryblok } from '@storyblok/nuxt-2'
  const { story, fetchState } = useStoryblok('home', { version: 'draft' })
</script>

<template>
  <StoryblokComponent v-if="story" :blok="story.content" />
</template>
    

At this point, the components should already be rendered successfully when viewing the Home story in the Visual Editor.

Nuxt app integrated into Storyblok Visual Editor

Nuxt app integrated into Storyblok Visual Editor

Real-time editing with Storyblok Bridge

The power of Storyblok relies on its fantastic real-time editing experience. Play with changing the teaser headline or re-arranging the features and see the magic happen!

Fortunately, @storyblok/nuxt makes it very easy for you. Your components have to be connected with Storyblok and listen to changes by its Visual Editor. Let's take a closer look at how this is achieved:

First, to link your Vue and Storyblok components together, @storyblok/nuxt automatically registers a v-editable directive. If you take a look at the components in your components/storyblok folder, you'll already find it there.

Second, useStoryblok loads the Storyblok Bridge under the hood by default.

If you want to learn more (or if you prefer working with the Options API), you can learn how to accomplish that in our docs on GitHub. Alternatively, you can check out the long form in our live demo.

Real-time editing experience enabled

Real-time editing experience enabled

Bonus: Dynamic Page Rendering

Right now, we’re just receiving the JSON content of our Home story in pages/index.vue. But what if we wanted to create more stories in our space? Luckily, in this case we would not have to create separate files for each story, resulting in a lot of superfluous code: Nuxt provides a really simple way to render pages dynamically by utilizing a pages/_.vue file.

Let’s quickly create it:

        
      <script setup>
import { useRoute } from '@nuxtjs/composition-api'
import { useStoryblok } from '@storyblok/nuxt-2'

const route = useRoute()
const slug = route.value.params.pathMatch

const { story, fetchState } = useStoryblok(slug ? slug : 'home', {
  version: 'draft',
})
</script>

<template>
  <StoryblokComponent v-if="story" :blok="story.content" />
</template>
    

So, what’s happening here? We’re dynamically getting the current slug via the useRoute function. This is then passed as a parameter to the useStoryblok function. If no slug is present, we want our Home story to be displayed (this is also why we set the Real path to / earlier).

Now, if you create another story and open it, it should work right away.

Dynamic page rendering in Nuxt allows for rapid content creation

Dynamic page rendering in Nuxt allows for rapid content creation

HINT:

You can learn to set up HTTPS for Nuxt2 for Windows OS.

Wrapping Up

Congratulations! You now have a Nuxt app with dynamic components, dynamic pages and a complete integration with Storyblok, providing a unique real-time editing experience.

WHAT'S NEXT?:

We (Storyblok) are huge fans of the Nuxt.js and the Vue.js universe. The Storyblok app is built in Vue.js and we are proud of it. As Nuxt.js is the all-in-one Vue.js meta-framework, we’ve built a Tech Hub to help you with the next steps on your Vue.js and Nuxt.js journey.

Authors

Samuel Snopko

Samuel Snopko

Samuel is the Head of Developer Relations at Storyblok with a passion for JAMStack and beautiful web. Co-creator of DX Meetup Basel and co-organizer of Frontend Meetup Freiburg. Recently fell in love with Vue.js, Nuxt.js, and Storyblok.

Manuel Schröder

Manuel Schröder

A former International Relations graduate, Manuel ultimately pursued a career in web development, working as a frontend engineer. His favorite technologies, other than Storyblok, include Vue, Astro, and Tailwind. These days, Manuel coordinates and oversees Storyblok's technical documentation, combining his technical expertise with his passion for writing and communication.