How to use Nuxt components inside Storyblok Rich-Text editor

Contents
    Try Storyblok

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

    I will talk about a feature of Storyblok that is not highlighted enough, yet is extremely powerful. The rich-text field, and more specifically, the possibility of inserting components.

    To provide some context, the rich-text at Storyblok is based on prosemirror.

    Why use Richtext with Storyblok?

    I am going to provide an example of how to use it and small tips that will bring you the added value for its use.

    Storyblok configuration

    You have a Nuxt project with storyblok-nuxt installed. From there you can start using the rich-text feature of Storyblok.

    Define components

    For the Storyblok part we will create a home page like in the image below.

    Storyblok editing capabilities

    The body part here is our focus-it will be our rich-text. The small configuration that I like to use when I write my articles is to disable the preview.

    Storyblok editing capabilities

    Before going into detail, we will first create the two components to use in our rich-text.

    Our blokDoubleImage Component

    The blokDoubleImage represents a block with 2 images taking 50% of the space.

    Storyblok editing capabilities

    Our blokSupport Component

    The blokSupport represents a banner in the flow of the page, composed of an URL, an image of the person and a description text .

    Storyblok editing capabilities

    For each component I suggest you use the preview functionality.

    Screenshot or icon:

    This option lets you define a screenshot that gets shown when the user inserts a new component in the blocks field. This helps the user to better identify the component type.

    Storyblok editing capabilities

    This gives you a mini-preview of your components which helps your users see what they are going to use.

    Example:

    Storyblok editing capabilities

    We will now configure and insert our two components in the rich-text.

    Storyblok editing capabilities
    Storyblok editing capabilities

    Et voila ! Now we can move on to Nuxt!

    Nuxt Configuration

    So now we want to do the following:

    • Prepare our components

    • Call the content of our home page

    • Render our rich text with the components.

    Prepare our components

    You must make sure that the name of the components will be the same as that on Storyblok.

    Example: BlokInfo should be BlockInfo too or blok-info.

    BlokDoubleImage.vue

    <template>
      <div class="flex -mx-2 my-4">
        <div class="px-2 w-1/2">
          <div
            class="bg-image"
            :style="{ backgroundImage: `url('${body.one.filename}')` }"
          ></div>
        </div>
        <div class="px-2 w-1/2">
          <div
            class="bg-image"
            :style="{ backgroundImage: `url('${body.two.filename}')` }"
          ></div>
        </div>
      </div>
    </template>
    
    <script>
    export default {
      props: {
        body: {
          type: Object,
          required: true,
        },
      },
    }
    </script>
    
    <style lang="postcss">
    ...
    </style>

    BlokSupport.vue

    <template>
      <div class="support w-full lg:w-1/2">
        <div class="rounded-lg shadow-xl p-4 flex">
          <div class="flex items-center w-1/3">
            <img
              :src="body.avatar.filename"
              :alt="body.avatar.alt"
              class="rounded-full object-cover"
            />
          </div>
          <div class="p-2 w-2/3">
            <p class="mb-4">{{ body.description }}</p>
            <a
              target="_blank"
              :href="body.url"
              class="bg-yellow-400 text-black px-4 py-2 rounded-lg flex items-center w-40"
            >
              <Coffee class="w-6 h-6 mr-2" />
              <span>Buy coffee</span>
            </a>
          </div>
        </div>
      </div>
    </template>
    
    <script>
    export default {
      props: {
        body: {
          type: Object,
          required: true,
        },
      },
    }
    </script>
    
    <style lang="postcss">
    ...
    </style>

    Call the content of our home page

    In your index.vue use the following code.

    index.vue

    <template>
      <div class="m-auto w-full md:w-2/3 px-4">
        <h1 class="text-6xl font-bold mb-4">{{ title }}</h1>
        <p class="text-lg mb-8">{{ description }}</p>
        <div class="mb-8">
          <SbRichText :text="body" />
        </div>
      </div>
    </template>
    
    <script>
    export default {
      async asyncData({ app }) {
        try {
          const { data } = await app.$storyapi.get(`cdn/stories/home`, {
            version: process.env.STORYBLOK_VERSION || 'draft',
          })
          return {
            ...data.story.content,
          }
        } catch (e) {
          console.error(e)
        }
      },
    }
    </script>

    Render our rich text with the components.

    First create the rich text component.

    SbRichText.vue

    <template>
      <div>
        <RichTextRenderer :document="text" />
      </div>
    </template>
    
    <script>
    export default {
      props: {
        text: {
          type: [String, Object],
          default: '',
        },
      },
      computed: {
        richtext() {
          return typeof this.text === 'string'
            ? this.text
            : this.$storyapi.richTextResolver.render(this.text)
        },
      },
    }
    </script>
    
    <style lang="postcss" scoped>
    ... your css for your rich text
    </style>

    By default, Storyblok's rich-text component is not able to render inline components. To do this you need a compiler. In our case, I suggest you use storyblok-rich-text-renderer.

    You will have to install it.

    yarn add --dev @marvr/storyblok-rich-text-vue-renderer
    
    yarn add --dev @vue/composition-api // It needs vue composition api to make it work.

    Create two plugins:

    plugins/composition-api.js

    import Vue from 'vue'
    import VueCompositionApi from '@vue/composition-api'
    
    Vue.use(VueCompositionApi)

    plugins/rich-text-renderer.js

    import Vue from 'vue'
    import VueRichTextRenderer from '@marvr/storyblok-rich-text-vue-renderer'
    import blokDoubleImage from '@/components/bloks/blok-double-image'
    import blokSupport from '@/components/bloks/blok-support'
    
    Vue.use(VueRichTextRenderer, {
      resolvers: {
        components: {
          blokDoubleImage,
          blokSupport,
        },
      },
    })

    Don't forget to update the config of the Nuxt.

    ./nuxt.config.js

    export default {
    	...
    	plugins: ['~/plugins/composition-api.js', '~/plugins/rich-text-renderer.js'],
    }
    IMPORTANT:

    You must follow the order exactly or it will not work.

    Conclusion

    Voila! Now your components should be rendered. You can create a full component library and give freedom to your content creator. The only limit is your imagination!