Rich Text Serializer
On this article you'll learn what a rich text serializer is, and when you can use it in your project.
In Prismic, rich text lets content writers format text with headings, bold styling, and more. Content is stored in a Prismic-specific format designed to convert to other formats, such as HTML. A rich text serializer defines how rich text content is converted and rendered on your website.
In Prismic, rich text is stored in an array. A rich text serializer is a function that defines how the elements of that array should be joined.
Prismic's development package, @prismicio/client, convert rich text content into HTML by default, so it’s not necessary to provide your own rich text serializer. However, you might want to change how rich text is serialized into HTML. In that case, you can provide your own rich text serializer to alter the serialization selectively.
There are two types of rich text serializer:
- An object rich text serializer
- A functional rich text serializer
You can use an object rich text serializer in implementations that depend on @prismicio/client, which includes all of our officially supported framework integrations except Vue.js and Nuxt.
An object rich text serializer is an object that uses methods to describe how Prismic's rich text elements should be rendered.
Each key on the rich text serializer object is the name of a rich text element (such as paragraph, label, or heading1) and the value should be a function that returns a converted value.
Here's a brief example. This code says, "if the type of the element is 'label,' return the text inside strong tags with a class of the label."
import * as prismic from '@prismicio/client'
import { PrismicRichText } from '@prismicio/react'
const components = {
label: ({ node, children }) => (
<strong className={node.data.label}>{children}</strong>
),
}
const Text = ({ slice }) => {
return (
<section>
{prismic.isFilled.richText(slice.primary.text) && (
<PrismicRichText field={slice.primary.text} components={components} />
)}
</section>
)
}
export default Text
import { graphql } from "gatsby";
import * as prismic from "@prismicio/client";
import { PrismicRichText } from "@prismicio/react";
const components = {
label: ({ node, children }) => (
<strong className={node.data.label}>{children}</strong>
),
}
const Text = ({ slice }) => {
return (
<section>
{prismic.isFilled.richText(slice.primary.text.richText) && (
<PrismicRichText field={slice.primary.text.richText} components={components} />
)}
</section>
);
};
export default Text;
export const fragment = graphql`
fragment PrismicText on PrismicText {
... on PrismicTextDefault {
variation
primary {
text {
richText
}
}
}
}
`;
import * as prismic from '@prismicio/client'
import { PrismicRichText } from '@prismicio/react'
const components = {
label: ({ node, children }) => (
<strong className={node.data.label}>{children}</strong>
),
}
const Text = ({ slice }) => {
return (
<section>
{prismic.isFilled.richText(slice.primary.text) && (
<PrismicRichText field={slice.primary.text} components={components} />
)}
</section>
)
}
export default Text
<!--
The Prismic integration for Nuxt doesn't not yet support
an object rich text serializer. See the functional serializer
documentation below.
-->
<!--
The object serializer is only available with Vue 3.
If you're using Vue 2, use the functional serializer,
as described below.
-->
<template>
<prismic-rich-text
:field="slice.primary.text"
:html-serializer="htmlSerializer"
/>
</template>
<script>
import { getSliceComponentProps } from "@prismicio/vue";
const htmlSerializer = {
label: ({children, key, type, node, text}) => (
`<strong class="${node.data.label}">${children}</strong>`
)
}
export default {
props: getSliceComponentProps(),
};
</script>
<script>
import * as prismic from '@prismicio/client'
const serializer = {
label: ({children, key, type, node, text}) => (
`<strong class="${node.data.label}">${children}</strong>`
)
}
export let slice
</script>
{@html prismic.asHTML(slice.primary.text, { serializer })}
import * as prismic from '@prismicio/client'
const serializer = {
label: ({children, key, type, node, text}) => (
`<strong class="${node.data.label}">${children}</strong>`
)
}
// `document` is a document from the Prismic API
prismic.asHTML(document.data.example_rich_text_field, { serializer })
The methods on the components object receive an params object with four parameters: children, key, type, node, text.
children
array
An array of all child elements, which have already been serialized. Example:
[ "Massa sapien faucibus ", "<strong>cras tincidunt lobortis</strong>", ". Pharetra pharetra massa ", "<span class=\"codespan\">ultricies</span>", " morbi tincidunt augue interdum." ]
key
string
A unique key for React's key prop.
node
object
A description of the entire element. For inline elements, this includes the start index, end index, type, and — if the element is a custom label — the name of the label. Example:
{ "start": 0, "end": 6, "type": "label", "data": { "label": "codespan" } }
For block-level elements (eg: paragraph, headings, pre), this includes the type, the text content, and an array containing all of the inline elements. Example:
{ type: 'paragraph', text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.', spans: [ { start: 63, end: 71, type: 'label', data: { label: 'codespan' } }, ] }
text
string
The content of the element. Example:
"Lorem ipsum"
type
string
The type of the element. Example: "span", "heading2", "paragraph", "image".
A rich text serializer function can be passed to a Prismic rich text helper or component. The function receives a rich text block, such as a paragraph or heading, and returns a converted version of it, such as an HTML representation of it. The function is called for each individual block in the rich text value.
Here's a brief example. This code says, "if the type of the element is 'label,' return the text inside strong tags with a className of the label."
import * as prismic from '@prismicio/client'
const serializer = (type, element, text, children) => {
if (type === 'label')
return `<strong className="${element.data.label}">${children}</strong>`
return null
}
const Text = ({ slice }) => {
return (
<section>
{prismic.isFilled.richText(slice.primary.text) && (
<PrismicRichText field={slice.primary.text.richText} htmlSerializer={serializer} />
)}
</section>
)
}
export default Text
import { graphql } from "gatsby";
import * as prismic from "@prismicio/client";
import { PrismicRichText } from "@prismicio/react";
const htmlSerializer = (type, element, text, children) => {
if (type === 'label')
return `<strong className="${element.data.label}">${children}</strong>`
return null
}
const Text = ({ slice }) => {
return (
<section>
{prismic.isFilled.richText(slice.primary.text.richText) && (
<PrismicRichText field={slice.primary.text.richText} htmlSerializer={htmlSerializer} />
)}
</section>
);
};
export default Text;
export const fragment = graphql`
fragment PrismicText on PrismicText {
... on PrismicTextDefault {
variation
primary {
text {
richText
}
}
}
}
`;
import * as prismic from '@prismicio/client'
const serializer = (type, element, text, children) => {
if (type === 'label')
return `<strong className="${element.data.label}">${children}</strong>`
return null
}
const Text = ({ slice }) => {
return (
<section>
{prismic.isFilled.richText(slice.primary.text) && (
<PrismicRichText field={slice.primary.text.richText} htmlSerializer={serializer} />
)}
</section>
)
}
export default Text
<template>
<prismic-rich-text
:field="slice.primary.text"
:html-serializer="htmlSerializer"
/>
</template>
<script>
export default {
name: 'Text',
props: {
slice: Object,
},
methods: {
htmlSerializer: function (type, element, text, children) {
if (type === 'label')
return `<strong className="${element.data.label}">${children}</strong>`
return null
},
},
}
</script>
<template>
<prismic-rich-text
:field="slice.primary.text"
:html-serializer="htmlSerializer"
/>
</template>
<script>
export default {
name: 'Text',
props: {
slice: Object,
},
methods: {
htmlSerializer: function (type, element, text, children) {
if (type === 'label')
return `<strong className="${element.data.label}">${children}</strong>`
return null
},
},
}
</script>
<script>
import * as prismic from '@prismicio/client'
export let slice
function serializer(type, element, text, children) {
if (type === 'label') {
return `<strong class="${element.data.label}">${children}</strong>`
}
return null
}
</script>
{@html prismic.asHTML(slice.primary.text, { serializer })}
import * as prismic from '@prismicio/client'
function serializer(type, element, text, children) {
if (type === 'label') {
return `<strong class="${element.data.label}">${children}</strong>`
}
return null
}
// `document` is a document from the Prismic API
prismic.asHTML(document.data.example_rich_text_field, { serializer })
The functional rich text serializer receives four parameters in the following order: type, element, content, children.
type
string
The type of the element. Example: "span", "heading2", "paragraph", "image".
element
object
A description of the entire element. For inline elements, this includes the start index, end index, type, and — if the element is a custom label — the name of the label. Example:
{ "start": 0, "end": 6, "type": "label", "data": { "label": "codespan" } }
For block-level elements (eg: paragraph, headings, pre), this includes the type, the text content, and an array containing all of the inline elements. Example:
{ type: 'paragraph', text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.', spans: [ { start: 63, end: 71, type: 'label', data: { label: 'codespan' } }, ] }
text
string
The text of the element. Example:
"Lorem ipsum"
children
array of strings
An array of all child elements, which have already been serialized. Example:
[ "Massa sapien faucibus ", "<strong>cras tincidunt lobortis</strong>", ". Pharetra pharetra massa ", "<span class=\"codespan\">ultricies</span>", " morbi tincidunt augue interdum." ]
Note that @prismicio/client v7 joins this array as a string by default, so the children parameter will look like this:
"Massa sapien faucibus, <strong>cras tincidunt lobortis</strong>. Pharetra pharetra massa <span class=\"codespan\">ultricies</span> morbi tincidunt augue interdum."
Sometimes the default rich text serializer doesn't cover your use case. Maybe you want to add a very precise set of class names to certain elements, or to add a unique HTML tag based on the label. That's when the rich text serializer comes in handy.
To learn more about how to use your rich text serializer in your project, see the documentation for your chosen framework.
To see how @prismicio/client can serialize every available element, view the example in the @prismicio/richtext Technical Reference or this simplified version of a functional rich text serializer handling all cases:
- Object
- Functional
const linkResolver = (doc) => '/' + doc.uid
const serializer = {
heading1: ({ children }) => `<h1>${children}</h1>`,
heading2: ({ children }) => `<h2>${children}</h2>`,
heading3: ({ children }) => `<h3>${children}</h3>`,
heading4: ({ children }) => `<h4>${children}</h4>`,
heading5: ({ children }) => `<h5>${children}</h5>`,
heading6: ({ children }) => `<h6>${children}</h6>`,
paragraph: ({ children }) => `<p>${children}</p>`,
preformatted: ({ node }) => `<pre>${JSON.stringify(node.text)}</pre>`,
strong: ({ children }) => `<strong>${children}</strong>`,
em: ({ children }) => `<em>${children}</em>`,
listItem: ({ children }) => `<li>${children}</li>`,
oListItem: ({ children }) => `<li>${children}</li>`,
list: ({ children }) => `<ul>${children}</ul>`,
oList: ({ children }) => `<ol>${children}</ol>`,
image: ({ node }) => {
const linkUrl = node.linkTo ? linkResolver(node.linkTo) : null
const linkTarget =
node.linkTo && node.linkTo.target
? `target="${node.linkTo.target}" rel="noopener"`
: ''
const wrapperClassList = [node.label || '', 'block-img']
const img = `<img src="${node.url}" alt="${
node.alt ? node.alt : ''
}" copyright="${node.copyright ? node.copyright : ''}" />`
return `
<p class="${wrapperClassList.join(' ')}">
${linkUrl ? `<a ${linkTarget} href="${linkUrl}">${img}</a>` : img}
</p>
`
},
embed: ({ node }) => `
<div data-oembed="${node.oembed.embed_url}"
data-oembed-type="${node.oembed.type}"
data-oembed-provider="${node.oembed.provider_name}"
${label(node)}>
${node.oembed.html}
</div>
`,
hyperlink: ({ node, children }) => {
const target = node.data.target
? `target="${node.data.target}" rel="noopener"`
: ''
const url = linkResolver(node.data)
return `<a ${target} href="${url}">${children}</a>`
},
label: ({ node, children }) => {
return `<span class="${node.data.label}">${children}</span>`
},
span: ({ text }) => (text ? text : ''),
}
function serializer(type, element, content, children) {
const linkResolver = (doc) => '/' + doc.uid
switch (type) {
case 'heading1':
return `<h1>${children}</h1>`
case 'heading2':
return `<h2>${children}</h2>`
case 'heading3':
return `<h3>${children}</h3>`
case 'heading4':
return `<h4>${children}</h4>`
case 'heading5':
return `<h5>${children}</h5>`
case 'heading6':
return `<h6>${children}</h6>`
case 'paragraph':
return `<p>${children}</p>`
case 'preformatted':
return `<pre>${JSON.stringify(element.text)}</pre>`
case 'strong':
return `<strong>${children}</strong>`
case 'em':
return `<em>${children}</em>`
case 'listItem':
return `<li>${children}</li>`
case 'oListItem':
return `<li>${children}</li>`
case 'list':
return `<ul>${children}</ul>`
case 'oList':
return `<ol>${children}</ol>`
case 'image':
const linkUrl = element.linkTo ? linkResolver(element.linkTo) : null
const linkTarget =
element.linkTo && element.linkTo.target
? `target="${element.linkTo.target}" rel="noopener"`
: ''
const wrapperClassList = [element.label || '', 'block-img']
const img = `<img src="${element.url}" alt="${
element.alt ? element.alt : ''
}" copyright="${element.copyright ? element.copyright : ''}" />`
return `
<p class="${wrapperClassList.join(' ')}">
${linkUrl ? `<a ${linkTarget} href="${linkUrl}">${img}</a>` : img}
</p>
`
case 'embed':
return `
<div data-oembed="${element.oembed.embed_url}"
data-oembed-type="${element.oembed.type}"
data-oembed-provider="${element.oembed.provider_name}"
${label(element)}>
${element.oembed.html}
</div>
`
case 'hyperlink':
const target = element.data.target
? `target="${element.data.target}" rel="noopener"`
: ''
const url = linkResolver(element.data)
return `<a ${target} href="${url}">${children}</a>`
case 'label':
return `<span class="${element.data.label}">${children}</span>`
case 'span':
return content ? content : ''
default:
return ''
}
}
Was this article helpful?
Can't find what you're looking for? Spot an error in the documentation? Get in touch with us on our Community Forum or using the feedback form above.