This is a preview of the Storyblok Website with Draft Content

Announcing Official Storyblok Richtext Support in our Frontend SDKs

Developers Draft
Try Storyblok

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

Last summer, we announced a brand new @storyblok/richtext package, offering the community a more powerful way to parse Richtext content into their websites and apps using Storyblok. Thanks to you, this new library was greatly adopted, reaching around 130k monthly downloads at the time of this writing.

Although the library can be used directly in your projects, it is about time you could benefit from Storyblok's major frontend SDK developer experience to use it. The time has come, and we are thrilled to present you Official Richtext Support for:

How to use

React/Next SDK

warn:

We have identified issues with richtext and Types on React 19 and Next.js 15. As a temporary measure, we advise you to continue using React 18 and Next.js 14 until we fully resolve the issues.

To begin using it on your Next projects, make sure to install the latest version:

        
      npm install @storyblok/react@latest
    

You can render rich text fields by using the StoryblokRichText component using the prop doc.

        
      import { StoryblokRichText, useStoryblok } from '@storyblok/react';

function App() {
  const story = useStoryblok('home', { version: 'draft' });

  if (!story?.content) {
    return <div>Loading...</div>;
  }

  return (
    <div>
      <StoryblokRichText doc={story.content.richText} />
    </div>
  );
}
    

Or you can have more control by using the useStoryblokRichText hook:

        
      import { useStoryblokRichText, convertAttributesInElement } from '@storyblok/react';
import Codeblock from './Codeblock';

function App() {
  const { render } = useStoryblokRichText({
    //Options like resolvers
  });

  const html = render(doc);
  const formattedHtml = convertAttributesInElement(html as React.ReactElement); // JSX

  return (
    <div ref={ref}>
      {formattedHtml}
    </div>
  );
}
    

For a comprehensive list of options you can provide to the useStoryblokRichText , please consult the Full options documentation.

Vue/Nuxt SDK

You can render rich text fields by using the StoryblokRichText component:

        
      <script setup>
  import { StoryblokRichText, useStoryblok } from "@storyblok/vue";
  
  const story = useStoryblok('home', { version: 'draft' });
</script>

<template>
  <StoryblokRichText v-if="story.content" :doc="story.content.richText" />
</template>
    

Or you can have more control by using the useStoryblokRichText composable:

        
      <script setup>
  import { useStoryblokRichText } from "@storyblok/vue";
  const { render } = useStoryblokRichText({
    // Options like resolvers
  });

  const root = () => render(story.content.richText);
</script>

<template>
  <root />
</template>
    

Overriding the default resolvers

It’s fairly a common use-case to replace the default resolvers (the functions that map Storyblok’s field types to the component to be rendered by the framework) with a custom one on your project, for example, using Next’s Link component as a replacement for all the anchors inside of the Richtext field or swapping Code Blocks for a custom local component:

        
      import { StoryblokRichText, useStoryblok, MarkTypes, type StoryblokRichTextNode } from '@storyblok/react';
import CodeBlock from './components/CodeBlock';

import Link from 'next/link';

const resolvers = {
	// Replace anchors with Next's Link
	[MarkTypes.LINK]: (node: StoryblokRichTextNode<ReactElement>) => {
	return node.attrs?.linktype === 'story'
	   ? (
	      <Link
	        href={node.attrs?.href}
	        target={node.attrs?.target}
	      >
	        {node.text}
	      </Link>
	    )
	  : (
	      <a
	        href={node.attrs?.href}
	        target={node.attrs?.target}
	      >
	        {node.text}
	      </a>
	    );
	},
	// Replace code blocks with a custom component
	[BlockTypes.CODE_BLOCK]: (node) =>
	<CodeBlock
	  class={node?.attrs?.class}
	>
	  {node.children}
	</CodeBlock>;
  }
  
  return (
    <div>
      <StoryblokRichText 
        doc={story.content.richText}
        resolvers={resolvers} 
      />
    </div>
  );
    

The same object can be passed to composables/hooks and the Vue component.

What about other SDKs?

Great, we now have official support for React/Next and Vue/Nuxt combos, but what about the other Storyblok SDKs like Astro or Svelte?

The quick answer is: that we are working on providing official support for the new Richtext package on these frameworks. Both have slightly different ways to render so we are still deciding the best way to integrate it. In the meantime, if the need arises, we suggest using NordVPN’s storyblok-rich-text-astro-renderer package which is highly adopted by the Astro + Storyblok community.

If you need a rich-text renderer for the Svelte SDK, please let us know via a request ticket on the official repo.

What about the previous Richtext approach?

As mentioned in the Richtext package announcement we will gradually sunset it in due time to facilitate the migration and adoption of the new approach, so for now, both solutions will co-exist in the ecosystem for a period of time until the next major version of storyblok-js-client (v7.0) lands.

Next Steps

We hope you are as excited as we are with the official Richtext support. We would absolutely love to see your projects built with Storyblok and hear your feedback

Want to contribute to this feature or do you have an issue to report? Feel free to create a new issue with a minimal reproduction in the correspondent repository listed below:

Happy OSS!

Resources