Skip to main content

Add a headless CMS to Nuxt 3 in 5 minutes

Contents
    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! Alternatively, you can explore or fork the code from the Nuxt Ultimate Tutorial GitHub Repository.

    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

    Important:

    The project in this tutorial and its subsequent parts was developed using the following versions:

    • nuxt@3.2.2
    • @storyblok/nuxt@5.1.0

    Please remember that these versions may be slightly behind the latest ones.

    Create a Nuxt project

    Following the Nuxt 3 official installation guide, we can easily create our project using the installation tool nuxi. Use it by running the following command:

    npx nuxi init <project-name>

    If you open your newly created project folder in your code editor of choice, you will notice that by default, Nuxt 3 uses TypeScript. However, since we would like to use simple JavaScript for this project, we can delete the tsconfig.json and rename nuxt.config.ts to nuxt.config.js.

    Once you installed the dependencies by running npm install , you can launch the project by running npm run dev. Now you’ll see this screen when you open http://localhost:3000 in your browser:

    localhost:3000
    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.

    app.storyblok.com
    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 US, please be mindful of the required API parameter explained hereinafter).

    app.storyblok.com
    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}:

    app.storyblok.com
    Opening the Home story
    1
    2

    Opening the Home story

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

    app.storyblok.com
    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:3000/ {2}:

    app.storyblok.com
    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 the instructions in our FAQ entry: How to set up SSL in Nuxt 3.

    Now, if you go back to the Home story, you won’t see your Nuxt app 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:

    app.storyblok.com
    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 3:

    npm install @storyblok/nuxt

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

    app.storyblok.com
    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 modules of nuxt.config.js :

    nuxt.config.js
    modules: [['@storyblok/nuxt', { accessToken: '<your-access-token-here>' }]],

    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 blocks)

    • 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 block

    • grid: Nested block

    • feature: Nested block

    • teaser: Nested block

    You can 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 a storyblok folder at the root of your project:

    storyblok/Page.vue
    <template>
      <div v-editable="blok" class="px-4">
        <StoryblokComponent v-for="blok in blok.body" :key="blok._uid" :blok="blok" />
      </div>
    </template>
    
    <script setup>
    defineProps({ blok: Object })
    </script>
    

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

    storyblok/Feature.vue
    <template>
      <div
        v-editable="blok"
        class="w-full p-12 bg-[#f7f6fd] rounded-[5px] text-center"
      >
        <h3 class="text-2xl text-[#1d243d] font-bold">
          {{ blok.name }}
        </h3>
      </div>
    </template>
    
    <script setup>
    defineProps({ blok: Object })
    </script>
    storyblok/Teaser.vue
    <template>
      <div
        v-editable="blok"
        class="py-32 text-6xl text-[#50b0ae] 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. No need to register your components – it doesn’t get any easier!

    Optional: Use TailwindCSS to style your components

    You may have noticed that we’ve been using Tailwind classes in our components. In order to make these work, let’s quickly add Tailwind to our Nuxt project by running:

    npm install @nuxtjs/tailwindcss

    Next, let’s add it to the modules in nuxt.config.js:

    nuxt.config.js
    modules: [
      ['@storyblok/nuxt', { accessToken: '<your-access-token-here>' }],
      '@nuxtjs/tailwindcss',
    ]

    hint:

    For spaces created in the United States, you have to set the region parameter accordingly: { apiOptions: { region: 'us' } }.

    Finally, we’ll have to create the following two files:

    tailwind.config.js
    module.exports = {
      content: ['storyblok/**/*.{vue,js}', 'components/**/*.{vue,js}', 'pages/**/*.vue'],
    }
    

    assets/css/tailwind.css
    @tailwind base;
    @tailwind components;
    @tailwind utilities;

    And that’s it! Let’s move on.

    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:

    app.storyblok.com
    Getting the Draft JSON from the Visual Editor
    1

    Getting the Draft JSON from the Visual Editor

    All we have to do is to create a pages/index.vue with the following code:

    pages/index.vue
    <script setup>
    const story = await useAsyncStoryblok('home', { version: 'draft' })
    </script>
    
    <template>
      <StoryblokComponent v-if="story" :blok="story.content" />
    </template>

    Having created this file, you can now delete the app.vue at the root of your project.

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

    app.storyblok.com
    Nuxt app integrated into the Storyblok Visual Editor

    Nuxt app integrated into the 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, useAsyncStoryblok loads the Storyblok Bridge under the hood by default.

    warn:

    For now, you can only 1 useAsyncStoryblok or useStoryblok composable per page because they’re loading the bridge. For more API calls, you can use useStoryblokApi.

    If you want to learn more (or if you prefer working with the Options API), have a look at our docs on GitHub. Alternatively, you can check out the long form in our SDK demo.

    app.storyblok.com
    Real-time editing experience enabled

    Real-time editing experience enabled

    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.

    Next Part:

    Continue reading and find out How to Render Storyblok Stories Dynamically in Nuxt.

    Resource Link
    Storyblok Nuxt 3 Ultimate Tutorial https://www.storyblok.com/tp/storyblok-nuxt-ultimate-tutorial
    Storyblok Nuxt 3 Module https://github.com/storyblok/storyblok-nuxt
    Storyblok Nuxt Technology Hub https://www.storyblok.com/tc/nuxtjs
    Storyblok APIs https://www.storyblok.com/docs/api
    Nuxt 3 https://v3.nuxtjs.org/

    Developer Newsletter

    Want to stay on top of the latest news and updates in Storyblok?
    Subscribe to Code & Bloks - our headless newsletter.

    An error occurred. Please get in touch with marketing@storyblok.com

    Please select at least one option.

    Please enter a valid email address.

    This email address is already registered.

    Please Check Your Email!

    Almost there! To confirm your subscription, please click on the link in the email we’ve just sent you. If you didn’t receive the email check your ’junk folder’ or