Almost EVERYONE who tried headless systems said they saw benefits. Download the state of CMS now!

Storyblok now on AWS Marketplace: Read more

O’Reilly Report: Decoupled Applications and Composable Web Architectures - Download Now

Empower your teams & get a 582% ROI: See Storyblok's CMS in action

Skip to main content

Creating Engaging Content in Nuxt with Storyblok & Storefront UI

Try Storyblok

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

  • Home
  • Tutorials
  • Creating Engaging Content in Nuxt with Storyblok & Storefront UI

Creating Engaging Content in Nuxt with Storyblok & Storefront UI is a powerful combination that allows you to build fast, dynamic, and engaging websites. Nuxt is a Vue.js framework that simplifies the development of complex web applications. Storyblok is a headless CMS that gives you complete control over your content, while Storefront UI is a library of pre-built UI components that can be easily integrated into your Nuxt project.

Another advantage of using Nuxt with Storyblok and Storefront UI is that it allows for faster development and deployment times. Nuxt's server-side rendering (SSR) capabilities ensure that your website loads quickly and is SEO-friendly. Storyblok's cloud-based CMS means that you don't have to worry about hosting or maintaining your own content management system. Storefront UI's pre-built components are designed to be easy to use and integrate, which means that your development team can work more efficiently.

In this article, I will show you how to use these amazing tools together to build engaging content and beautiful websites.

If you would like to see the demo instantly, check out the stackblitz demo https://stackblitz.com/edit/nuxt-3-sdk-demo-mked9c?file=pages%2Findex.vue

Section titled Storyblok Storyblok

One of the key benefits of Storyblok is that it allows for easy content management, even for non-technical users. This means that your marketing team can create and publish content without having to rely on developers to update the website. With Storyblok's powerful API, you can also build dynamic content-driven applications that can pull content from multiple sources.

In this article, I will be using the @storyblok/nuxt module that works great in Nuxt ecosystem.


The installation is pretty straightforward. You just need to install the module like the following:

        
      yarn add @storyblok/nuxt

    

And next, add it to the modules array of nuxt.config.ts file:

        
      export default defineNuxtConfig({
  modules: [
    ["@storyblok/nuxt", { accessToken: "<your-access-token>" }]
    // ...
  ]
});
    

And that’s it! You can start using Storyblok in your application like the following:

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

Section titled Storefront UI Storefront UI

Storefront UI provides a comprehensive library of pre-built UI components that can be easily customized and integrated into your Nuxt project. This means that you can save time and money by not having to build every UI component from scratch. Storefront UI includes everything from buttons and forms to complex UI components like product carousels and checkout flows.

The main idea of the Storefront UI component library is to use it in e-commerce projects but the amount of pre-built components is so big that you can use it in several different projects as well!

Check out the official documentation https://docs.storefrontui.io/v2/

Installation of Storefront UI in Nuxt application is pretty straightforward. First, let’s install the required packages:

        
      yarn add -D @nuxtjs/tailwindcss @storefront-ui/vue
    

Next, let’s add @nuxtjs/tailwindcss to the modules array in the nuxt.config.ts file:

        
      // nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@nuxtjs/tailwindcss']
})
    

Add / Create tailwind.config.ts with the following structure:

        
      // tailwind.config.ts
import type { Config } from 'tailwindcss';
import { tailwindConfig } from '@storefront-ui/vue/tailwind-config';
export default <Config>{
  presets: [tailwindConfig],
  content: ['./**/*.vue', './node_modules/@storefront-ui/vue/**/*.{js,mjs}'],
};
    

And that’s it! You can start using the components like the following:

        
      <template>
  <SfButton type="button" class="w-full"> Hello </SfButton>
</template>
<script lang="ts" setup>
import { SfButton } from '@storefront-ui/vue';
</script>
    

Storefront UI comes with many pre-built components for you and you can check them all out by visiting the documentation and the playground:

All components are designed in a preview/code way which means that you can easily copy the source code of the elements that you like directly into your project!

Section titled Using it all in your next (nuxt) project Using it all in your next (nuxt) project

By combining Storyblok and Storefront UI, you can build dynamic and engaging websites quickly and easily. With Storyblok's content management system and Storefront UI's pre-built components, your team can focus on building the best user experience possible. The end result is a website that is not only visually appealing but also highly functional and easy to navigate.

In this example, I will be using two major components for displaying content from Storyblok: Banner and Card. Obviously, you can use more, but in this case, I wanted to show the basic usage that you can just copy and use on your website.

Let’s start with the Banner. I will copy the code from the official documentation that I will later refactor to use the data from Storyblok.

        
      <template>
  <div class="flex flex-col md:flex-row flex-wrap gap-6 max-w-[1540px]">
    <div
      v-for="{
        image,
        title,
        subtitle,
        description,
        buttonText,
        backgroundColor,
        reverse,
        titleClass,
        subtitleClass,
      } in displayDetails"
      :key="title"
      :class="[
        'relative flex md:max-w-[1536px] md:[&:not(:first-of-type)]:flex-1 md:first-of-type:w-full',
        backgroundColor,
      ]"
    >
      <a
        class="absolute w-full h-full z-1 focus-visible:outline focus-visible:rounded-lg"
        :aria-label="title"
        href="#"
      />
      <div :class="['flex justify-between overflow-hidden grow', { 'flex-row-reverse': reverse }]">
        <div class="flex flex-col justify-center items-start p-6 lg:p-10 max-w-1/2">
          <p :class="['uppercase typography-text-xs block font-bold tracking-widest', subtitleClass]">
            {{ subtitle }}
          </p>
          <h2 :class="['mb-4 mt-2 font-bold typography-display-3', titleClass]">
            {{ title }}
          </h2>
          <p class="typography-text-base block mb-4">
            {{ description }}
          </p>
          <SfButton class="!bg-black">{{ buttonText }}</SfButton>
        </div>
        <img :src="image" :alt="title" class="w-1/2 self-end object-contain" />
      </div>
    </div>
  </div>
</template>
<script lang="ts" setup>
import { SfButton } from '@storefront-ui/vue';
const displayDetails = [
  {
    image: 'https://storage.googleapis.com/sfui_docs_artifacts_bucket_public/production/display.png',
    title: 'Sunny Days Ahead',
    subtitle: 'Be inspired',
    description: 'Step out in style with our sunglasses collection',
    buttonText: 'Discover now',
    reverse: false,
    backgroundColor: 'bg-negative-200',
    titleClass: 'md:typography-display-2',
    subtitleClass: 'md:typography-headline-6',
    descriptionClass: 'md:typography-text-lg',
  }
];
</script>
    

Let’s now replace the static displayDetails array of banners with dynamic data fetched from Storyblok. First, we need to create a banner component in Storyblok like the following:

Now, let’s add those banner components to our website

And let’s replace the code so that we can render the content from Storyblok:

        
      <template>
  <div class="flex flex-col md:flex-row flex-wrap gap-6 max-w-[1540px]">
    <div
      v-for="{
        image,
        title,
        subtitle,
        description,
        buttonText,
        reverse,
      } in story?.content?.body"
      :key="title"
      class="relative flex md:max-w-[1536px] md:[&:not(:first-of-type)]:flex-1 md:first-of-type:w-full"
    >
      <a
        class="
          absolute
          w-full
          h-full
          z-1
          focus-visible:outline focus-visible:rounded-lg
        "
        :aria-label="title"
        href="#"
      />
      <div
        :class="[
          'flex justify-between overflow-hidden grow',
          { 'flex-row-reverse': reverse },
        ]"
      >
        <div
          class="flex flex-col justify-center items-start p-6 lg:p-10 max-w-1/2"
        >
          <p
            class="uppercase typography-text-xs block font-bold tracking-widest',"
          >
            {{ subtitle }}
          </p>
          <h2 class="mb-4 mt-2 font-bold typography-display-3">
            {{ title }}
          </h2>
          <p class="typography-text-base block mb-4">
            {{ description }}
          </p>
          <SfButton class="!bg-black">{{ buttonText }}</SfButton>
        </div>
        <img :src="image.filename" :alt="title" class="w-1/2 self-end object-contain" />
      </div>
    </div>
  </div>
</template>
<script lang="ts" setup>
import { SfButton } from '@storefront-ui/vue';
const story = await useAsyncStoryblok('home', { version: 'draft' });
</script>
    

If we inspect the browser, you will see that we have these nice looking banners that we fetched the data from Storyblok!

Now, let’s go into the Card component. By default the code for Card component looks like this:

        
      <template>
  <div class="flex flex-wrap gap-4 lg:gap-6 lg:flex-nowrap">
    <div
      v-for="({ image, title, description, button }, index) in cardDetails"
      :key="`${title}-${index}`"
      class="flex flex-col min-w-[325px] max-w-[375px] lg:w-[496px] relative border border-neutral-200 rounded-md hover:shadow-xl"
    >
      <a
        class="absolute inset-0 z-1 focus-visible:outline focus-visible:outline-offset focus-visible:rounded-md"
        href="#"
      />
      <img :src="image" :alt="title" class="object-cover h-auto rounded-t-md aspect-video" />
      <div class="flex flex-col items-start p-4 grow">
        <p class="font-medium typography-text-base">{{ title }}</p>
        <p class="mt-1 mb-4 font-normal typography-text-sm text-neutral-700">{{ description }}</p>
        <SfButton size="sm" variant="tertiary" class="relative mt-auto">{{ button }}</SfButton>
      </div>
    </div>
  </div>
</template>
<script lang="ts" setup>
import { SfButton } from '@storefront-ui/vue';
const cardDetails = [
  {
    image: 'https://storage.googleapis.com/sfui_docs_artifacts_bucket_public/production/card-3.png',
    title: 'Sip Sustainably: The Rise of Boxed Water',
    description:
      'Boxed water is a sustainable alternative to traditional plastic bottles, made from renewable resources.',
    button: 'Read more',
  }
];
</script>
    

Let’s create a new component in Storyblok for card:

Now, let’s add the cards to our content page:

And replace the previous code with the following:

        
      <template>
  <div class="flex flex-wrap gap-4 lg:gap-6 lg:flex-nowrap">
    <div
      v-for="({ image, title, description, button }, index) in story?.content.body"
      :key="`${title}-${index}`"
      class="flex flex-col min-w-[325px] max-w-[375px] lg:w-[496px] relative border border-neutral-200 rounded-md hover:shadow-xl"
    >
      <a
        class="absolute inset-0 z-1 focus-visible:outline focus-visible:outline-offset focus-visible:rounded-md"
        href="#"
      />
      <img :src="image.filename" :alt="title" class="object-cover h-auto rounded-t-md aspect-video" />
      <div class="flex flex-col items-start p-4 grow">
        <p class="font-medium typography-text-base">{{ title }}</p>
        <p class="mt-1 mb-4 font-normal typography-text-sm text-neutral-700">{{ description }}</p>
        <SfButton size="sm" variant="tertiary" class="relative mt-auto">{{ button }}</SfButton>
      </div>
    </div>
  </div>
</template>
<script lang="ts" setup>
import { SfButton } from '@storefront-ui/vue';
const story = await useAsyncStoryblok('cards', { version: 'draft' });
</script>
    

Finally, let’s inspect the browser to see the components that are powered by the data from Storyblok.

And we have added those two big components in a matter of minutes. How cool is that? :)

Section titled Summary Summary

In summary, using Nuxt with Storyblok and Storefront UI is a powerful combination that allows you to build fast, dynamic, and engaging websites. With Storyblok's easy content management system, Storefront UI's pre-built UI components, and Nuxt's SSR capabilities, you can create websites that are visually appealing, highly functional, and easy to navigate. Whether you're building an e-commerce website or a content-driven blog, Nuxt with Storyblok and Storefront UI is a great choice for your next project.

Author

Jakub Andrzejewski

Jakub Andrzejewski

Senior Developer @VueStorefront, Ambassador @Nuxt.js and @Storyblok. Apart from work, Technical and Recreational Diver, Mountain Hiker, occasional gamer, and a huge fan of Lego and Transformers.