⚠️Draft Content
Storyblok
Search Storyblok's Documentation
  1. @storyblok/richtext

@storyblok/richtext

@storyblok/richtext a custom resolver for the Storyblok rich text field to be used in JavaScript applications.

Installation

Add the package to a project by running this command in the terminal:

npm install @storyblok/richtext@latest

Usage

Basic

import { richTextResolver } from '@storyblok/richtext';

const { render } = richTextResolver();

const html = render(doc);

document.querySelector<HTMLDivElement>('#app')!.innerHTML = `
  <div>
  ${html}
  </div>
`;

Overwrite resolvers

import { MarkTypes, richTextResolver } from '@storyblok/richtext';

const html = richTextResolver({
	resolvers: {
		[MarkTypes.LINK]: (node) => {
			return `<button href="${node.attrs?.href}" target="${node.attrs?.target}">${node.children}</button>`;
		},
	},
}).render(doc);

Optimize images

To optimize images, use the optimizeImages property on the richTextResolver options. For a full list of available options, refer to the Image Service documentation.

import { richTextResolver } from '@storyblok/richtext';

const html = richTextResolver({
	optimizeImages: {
		class: 'my-peformant-image',
		loading: 'lazy',
		width: 800,
		height: 600,
		srcset: [400, 800, 1200, 1600],
		sizes: ['(max-width: 400px) 100vw', '50vw'],
		filters: {
			format: 'webp',
			quality: 10,
			grayscale: true,
			blur: 10,
			brightness: 10,
		},
	},
}).render(doc);

Markdown to Storyblok rich text

Introduced in 3.6.0

The package now includes a powerful utility for converting Markdown content to Storyblok's rich text format, which can be rendered with the existing richTextResolver.

Supported markdown elements:

  • Text formatting: **bold**, *italic*, ~~strikethrough~~, `` code ``, [links](url)
  • Headings: # H1 through ###### H6
  • Lists: - unordered and 1. ordered lists with nesting
  • Code blocks: ``` ```fenced``` ``` and indented blocks
  • Blockquotes: > quoted text
  • Images: ![alt](src "title")
  • Links: [text](url) and [text](url "title")
  • Tables: Standard markdown table syntax
  • Horizontal rules: ---
  • Line breaks: (two spaces) for hard breaks
import { markdownToStoryblokRichtext } from '@storyblok/richtext';

const markdown = `
# Main Heading

This is a **bold** paragraph with *italic* text.

- List item 1
- List item 2

> This is a blockquote
`;

const richtextDoc = markdownToStoryblokRichtext(markdown);

// Convert to HTML using the existing richTextResolver
const html = richTextResolver().render(richtextDoc);
document.getElementById('content').innerHTML = html;

Similar to overwriting resolves via richtext options, you can customize how specific Markdown elements are converted by providing custom resolvers:

import { markdownToStoryblokRichtext, MarkdownTokenTypes } from '@storyblok/richtext';

const markdown = '# Custom Heading\nThis is a paragraph with [a link](https://example.com).';

const richtextDoc = markdownToStoryblokRichtext(markdown, {
  resolvers: {
    // Custom link resolver
    [MarkdownTokenTypes.LINK]: (token, children) => {
      return {
        type: 'link',
        attrs: {
          href: token.attrGet('href'),
          title: token.attrGet('title') || null,
          target: '_blank', // Always open in new tab
        },
        content: children,
      };
    },
  },
});

Framework usage

The @storyblok/richtext package is framework-agnostic and can be used with any JavaScript-based frontend framework. Below are examples of how to use the package with different frameworks.

React

For a better developer experience, use the corresponding APIs available in the framework SDK, such as the StoryblokRichText component in @storyblok/react. Learn more in the @storyblok/react package reference.

import React from 'react';
import { richTextResolver } from '@storyblok/richtext';

const options: StoryblokRichTextOptions<ReactElement> = {
	renderFn: React.createElement,
	keyedResolvers: true,
};

const html = richTextResolver(options).render(doc);

// Convert attributes in element to JSX. Refer to the `convertAttributesInElement` function in the playground/react
const formattedHtml = convertAttributesInElement(html);

return <>{formattedHtml}</>;

Refer to playground/react in the package repository for a complete example.

Vue

For a better developer experience, use the corresponding APIs available in the framework SDK, such as the StoryblokRichText component in @storyblok/vue. Learn more in the @storyblok/vue package reference.

<script setup>
  import type { VNode } from 'vue';
  import { createTextVNode, h } from 'vue';
  import { BlockTypes, richTextResolver, type StoryblokRichTextNode, type StoryblokRichTextOptions } from '@storyblok/richtext';

  const options: StoryblokRichTextOptions<VNode> = {
    renderFn: h,
    textFn: createTextVNode,
    keyedResolvers: true,
  };

  const root = () => richTextResolver<VNode>(options).render(doc);
</script>

<template>
	<root />
</template>

Refer to playground/vue in the package repository for a complete example.

TypeScript Generics

Correct type support in a framework-agnostic way is ensured by using Typescript Generics, circumventing the need to import types and require framework packages as dependencies.

Vanilla: string

const options: StoryblokRichTextOptions<string> = {
	resolvers: {
		[MarkTypes.LINK]: (node: Node<string>) => {
			return `<button href="${node.attrs?.href}" target="${node.attrs?.target}">${node.children}</button>`;
		},
	},
};

const html = richTextResolver < string > options.render(doc);

React: React.ReactElement

const options: StoryblokRichTextOptions<React.ReactElement> = {
	renderFn: React.createElement,
	keyedResolvers: true,
};
const root = () => richTextResolver < React.ReactElement > options.render(doc);

Vue: VNode

const options: StoryblokRichTextOptions<VNode> = {
	renderFn: h,
	keyedResolvers: true,
};
const root = () => richTextResolver < VNode > options.render(doc);


Further resources

@storyblok/richtext HTML Sanitization Tutorial Opens in new tab

Read the tutorial to learn how to sanitize the HTML string output of the rich text resolver.