@storyblok/richtext
@storyblok/richtext is a custom resolver for Storyblok rich text in JavaScript applications.
Installation
Section titled “Installation”Add the package to a project by running this command in the terminal:
npm install @storyblok/richtext@latestimport { richTextResolver } from '@storyblok/richtext'; const { render } = richTextResolver(); const html = render(doc); document.querySelector<HTMLDivElement >('#app')!.innerHTML = ` <div>${html}</div> `;</HTMLDivElement>Override resolvers
Section titled “Override 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);Resolvers context
Section titled “Resolvers context”You can access the resolver context via param when overriding a resolver. This give you access to:
originalResolversmergedResolvers(after the overrides)- Internal
rendermethod
import { MarkTypes, richTextResolver } from '@storyblok/richtext';const html = richTextResolver({resolvers: { [BlockTypes.LIST_ITEM]: (node: StoryblokRichTextNode<string>, ctx) => { // Custom list item with enhanced styling const children = node.children || ''; return ctx.render('li', { class: 'custom-list-item', 'data-item': 'true' }, `<span class="bullet">→</span> ${children}`); },},}).render(doc);Optimize images
Section titled “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
Section titled “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:
# H1through###### H6 - Lists:
- unorderedand1. ordered listswith nesting - Code blocks:
```fenced```and indented blocks - Blockquotes:
> quoted text - Images:
 - 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/markdown-parser";
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 richTextResolverconst html = richTextResolver().render(richtextDoc);document.getElementById("content").innerHTML = html;Similar to overwriting resolves via rich text options, you can customize how specific Markdown elements are converted by providing custom resolvers:
import { markdownToStoryblokRichtext, MarkdownTokenTypes } from "@storyblok/richtext/markdown-parser";
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, }; }, },});HTML to Storyblok rich text
Section titled “HTML to Storyblok rich text”Introduced in 3.7.0
The package includes a utility for converting HTML content to Storyblok’s rich text format, which can be rendered with the existing richTextResolver.
Supported HTML elements:
- Headlines:
<h1-6> - Paragraphs:
<p> - Lists:
<ol>,<ul>,<li> - Tables:
<table>,<thead>,<tbody>,<tr>,<th>,<td> - Blockquote:
<blockquote> - Code:
<pre>,<code> - Links:
<a> - Formatting:
<strong>,<b>,<em>,<i>,<del>,<s> - Images:
<img> - Misc:
<span>,<hr>,<br>
import { htmlToStoryblokRichtext } from "@storyblok/richtext/html-parser";
const html = `<h1>Main Heading</h1>
<p>This is a <strong>bold</strong> paragraph with <em>italic</em> text.</p>
<ul> <li>List item 1</li> <li>List item 2</li></ul>
<blockquote> <p>This is a blockquote</p></blockquote>`;
const richtextDoc = htmlToStoryblokRichtext(html);
// Convert to HTML using the existing richTextResolverconst html = richTextResolver().render(richtextDoc);document.getElementById("content").innerHTML = html;Similar to overwriting resolves via richtext options, you can customize how specific HTML elements are converted by providing custom resolvers:
import { htmlToStoryblokRichtext, HTMLTags } from "@storyblok/richtext/html-parser";
const html = '<h1>Custom Heading\nThis is a paragraph with <a href="https://example.com">a link</a>.</h1>';
const richtextDoc = htmlToStoryblokRichtext(html, { resolvers: { // Custom link resolver [HTMLTags.A]: (node, content) => { return { type: "link", attrs: { href: node.attrs.href, title: node.attrs.title || null, target: "_blank", // Always open in new tab }, content, }; }, },});Framework usage
Section titled “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.
import React from 'react';import { richTextResolver } from '@storyblok/richtext';
const options: StoryblokRichTextOptions<ReactElement> = {renderFn: React.createElement,keyedResolvers: true,};
function Example({ doc }) { const html = richTextResolver(options).render(doc); return <>{formattedHtml}</>;}Refer to playground/react in the @storyblok/richtext package repository for a complete example.
<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 @storyblok/richtext package repository for a complete example.
TypeScript Generics
Section titled “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
Section titled “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
Section titled “React: React.ReactElement”const options: StoryblokRichTextOptions<React.ReactElement> = {renderFn: React.createElement,keyedResolvers: true,};const root = () => richTextResolver < React.ReactElement > options.render(doc);Vue: VNode
Section titled “Vue: VNode”const options: StoryblokRichTextOptions<VNode> = {renderFn: h,keyedResolvers: true,};const root = () => richTextResolver < VNode > options.render(doc);Further resources
Section titled “Further resources”Get in touch with the Storyblok community