HTML Serializer
On this article you'll learn what an HTML 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. An HTML Serializer defines how Rich Text content is converted and rendered on your website.
In Prismic, Rich Text is stored in an array. An HTML Serializer is a function that defines how the elements of that array should be joined.
Prismic's development package, @prismicio/helpers, convert Rich Text content into HTML by default, so it’s not necessary to provide your own HTML Serializer. However, you might want to change how Rich Text is serialized into HTML. In that case, you can provide your own HTML Serializer to alter the serialization selectively.
There are two types of HTML Serializer:
- An object HTML Serializer
- A functional HTML Serializer
You can use an object HTML Serializer in implementations that depend on @prismicio/helpers, which includes all of our officially supported framework integrations except Vue.js and Nuxt.
An object HTML Serializer is an object that uses methods to describe how Prismic's Rich Text elements should be rendered.
Each key on the HTML 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 prismicH from '@prismicio/helpers'
import { PrismicRichText } from '@prismicio/react'
const components = {
label: ({ node, children }) => (
<strong className={node.data.label}>{children}</strong>
),
}
const Text = ({ slice }) => {
return (
<section>
{prismicH.isFilled.richText(slice.primary.text) && (
<PrismicRichText field={slice.primary.text} components={components} />
)}
</section>
)
}
export default Text
import { graphql } from "gatsby";
import * as prismicH from "@prismicio/helpers";
import { PrismicRichText } from "@prismicio/react";
const components = {
label: ({ node, children }) => (
<strong className={node.data.label}>{children}</strong>
),
}
const Text = ({ slice }) => {
return (
<section>
{prismicH.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 prismicH from '@prismicio/helpers'
import { PrismicRichText } from '@prismicio/react'
const components = {
label: ({ node, children }) => (
<strong className={node.data.label}>{children}</strong>
),
}
const Text = ({ slice }) => {
return (
<section>
{prismicH.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 HTML 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 prismicH from "@prismicio/helpers"
const htmlSerializer = {
label: ({children, key, type, node, text}) => (
`<strong class="${node.data.label}">${children}</strong>`
)
}
export let slice
</script>
{@html prismicH.asHTML(slice.primary.text, null, htmlSerializer)}
import * as prismicH from "@prismicio/helpers"
const htmlSerializer = {
label: ({children, key, type, node, text}) => (
`<strong class="${node.data.label}">${children}</strong>`
)
}
// `document` is a Document from the Prismic API
prismicH.asHTML(document.data.example_rich_text_field, null, htmlSerializer)
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".
An HTML 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 prismicH from '@prismicio/helpers'
const htmlSerializer = (type, element, text, children) => {
if (type === 'label')
return `<strong className="${element.data.label}">${children}</strong>`
return null
}
const Text = ({ slice }) => {
return (
<section>
{prismicH.isFilled.richText(slice.primary.text) && (
<PrismicRichText field={slice.primary.text.richText} htmlSerializer={htmlSerializer} />
)}
</section>
)
}
export default Text
import { graphql } from "gatsby";
import * as prismicH from "@prismicio/helpers";
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>
{prismicH.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 prismicH from '@prismicio/helpers'
const htmlSerializer = (type, element, text, children) => {
if (type === 'label')
return `<strong className="${element.data.label}">${children}</strong>`
return null
}
const Text = ({ slice }) => {
return (
<section>
{prismicH.isFilled.richText(slice.primary.text) && (
<PrismicRichText field={slice.primary.text.richText} htmlSerializer={htmlSerializer} />
)}
</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 prismicH from "@prismicio/helpers"
export let slice
function htmlSerializer(type, element, text, children) {
if (type === 'label') {
return `<strong class="${element.data.label}">${children}</strong>`
}
return null
}
</script>
{@html prismicH.asHTML(slice.primary.text, null, htmlSerializer)}
import * as prismicH from "@prismicio/helpers"
function htmlSerializer(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
prismicH.asHTML(document.data.example_rich_text_field, null, htmlSerializer)
The functional HTML 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/helpers v2 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 HTML 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 HTML Serializer comes in handy.
To learn more about how to use your HTML Serializer in your project, see the documentation for your chosen framework.
To see how @prismicio/helpers can serialize every available element, view the example in the @prismicio/richtext Technical Reference or this simplified version of a functional HTML Serializer handling all cases:
- Object
- Functional
const linkResolver = (doc) => '/' + doc.uid
const htmlSerializer = {
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 htmlSerializer(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.