Building Engaging Content in Nuxt with Storyblok and Storefront UI
Tags: nuxt, vue, javascript, webdev
Published: 2023-09-11 08:34:06

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

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

![Storyblok Nuxt](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ap289jo2eery7tcvckp7.png)

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

```html
yarn add @storyblok/nuxt
```

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

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

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

```html
<script setup>
  const story = await useAsyncStoryblok("vue", { version: "draft" });
</script>

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

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

![Storefront UI](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i5b8xtbd71pocs9rcex2.png)

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:

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

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

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

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

```ts
// 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 following:

```html
<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 component for you and you can check them all out by visiting the documentation and the playground:

![Storefront UI Docs](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x1fn0yf231pvp7e2u7h3.png)

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!

## 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 in your website.

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

```html
<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',
  },
  {
    image: 'https://storage.googleapis.com/sfui_docs_artifacts_bucket_public/production/display-2.png',
    title: 'Pack it Up',
    subtitle: 'Be active',
    description: 'Explore the great outdoors with our backpacks',
    buttonText: 'Discover now',
    reverse: true,
    backgroundColor: 'bg-warning-200',
  },
  {
    image: 'https://storage.googleapis.com/sfui_docs_artifacts_bucket_public/production/display-3.png',
    title: 'Fresh and Bold',
    subtitle: 'New collection',
    description: 'Add a pop up color to your outfit',
    buttonText: 'Discover now',
    reverse: false,
    backgroundColor: 'bg-secondary-200',
  },
];
</script>
```

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

![Storyblok Components](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k0x2lm9ls9gmqqkpqkeo.png)

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

![Storyblok Banner Component](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q3y0f4omk1aflr1yh977.png)

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

```html
<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!

![Stackblitz result](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ol05xofzvtoiwpwkz155.png)

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

```html
<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',
  },
  {
    image: 'https://storage.googleapis.com/sfui_docs_artifacts_bucket_public/production/card-2.png',
    title: 'Ride the Future: Exploring the Benefits of e-Bikes',
    description:
      'Eco-friendly, efficient, and fun modes of transportation that provide a range of benefits for riders and the environment.',
    button: 'Read more',
  },
  {
    image: 'https://storage.googleapis.com/sfui_docs_artifacts_bucket_public/production/card-1.png',
    title: 'Unleash the Ultimate Listening Experience',
    description:
      'Audiophile headphones offer unmatched sound quality and clarity, making them the go-to choice for music enthusiasts.',
    button: 'Read more',
  },
];
</script>
```

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

![Storyblok Card Component](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j9bf393s0q8861z8b9ez.png)

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

![Cards Content in Storyblok](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gq0zyn87pjiu049enrh5.png)

And replace the previous code with the following:

```html
<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.

![Stackblitz Result with Cards](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ufuxauky6zzwm6aa2poz.png)

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

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