Converting Inline Styles to Sanity Block Decorators
When migrating rich text content from other CMS platforms into Sanity, you may encounter inline styles like <span style="font-weight: bold;">
. By default, Sanity's html-to-blocks
method doesn't handle these styles, leading to loss of formatting in the converted content. This guide walks you through creating custom deserialization rules to properly handle these cases. We'll also cover how to process nested spans, preserve spaces between words, and merge decorators effectively. By the end, you'll have a robust solution for rich text migration without losing valuable formatting.
When migrating rich text content from another CMS to Sanity, inline styles (e.g., <span style="font-weight: bold;">
) often need to be translated into decorators like strong
, em
, or underline
. This guide walks you through customizing the html-to-blocks
serialization to handle such cases, including nested spans with multiple styles.
- Familiarity with Sanity's block content structure.
- Installed
@sanity/block-tools
package.
By default, the html-to-blocks
method handles common tags like <strong>
and <em>
, but inline styles like <span style="font-weight: bold;">
are ignored. To convert these spans into appropriate decorators, we need to extend the deserialization rules.
Custom Deserialization Rules
The following code demonstrates how to handle spans with inline styles, including nested spans:
const customRules = [
{
deserialize(el, next) {
if (el.tagName === 'SPAN') {
const style = el.style
const marks = []
// Collect marks from inline styles
if (style?.fontWeight === 'bold' || style?.fontWeight >= 600) {
marks.push('strong')
}
if (style?.fontStyle === 'italic') {
marks.push('em')
}
if (style?.textDecoration.includes('underline')) {
marks.push('underline')
}
// Initialize an array to hold the final processed spans
const processedSpans = []
// Process child nodes recursively
Array.from(el.childNodes).forEach((node) => {
if (node.nodeType === 3) {
// Handle text nodes
const text = node.nodeValue
if (text) {
processedSpans.push({
_type: 'span',
text,
marks,
})
}
} else {
// Process child elements recursively
const childNodes = next([node]).map((child) => {
if (child._type === 'span') {
return {
...child,
marks: [...new Set([...(child.marks || []), ...marks])],
}
}
return child
})
processedSpans.push(...childNodes)
}
})
return processedSpans
}
return undefined // Pass to the next rule if not a span
},
},
]
- Handles Inline Styles: Detects and converts
font-weight
,font-style
, andtext-decoration
styles into Sanity decorators. - Supports Nested Spans: Processes nested spans by merging inherited and child decorators.
- Prevents Redundant Marks: Uses
Set
to ensure each mark is applied only once.
- Marks Collection: The
style
property of the<span>
tag is inspected to determine which marks to apply. - Child Node Processing: Text nodes are wrapped into spans, and child elements are recursively processed with the accumulated marks.
- Nested Styles: For child elements, existing marks are merged with those inherited from the parent.
To ensure your custom rules work as expected, test the following HTML input:
<span style="font-weight: bold;">Want to <span style="font-style: italic;">learn <span style="text-decoration: underline;">a lot</span> more</span></span>
This should output:
[
{
"_type": "span",
"text": "Want to ",
"marks": ["strong"]
},
{
"_type": "span",
"text": "learn ",
"marks": ["strong", "em"]
},
{
"_type": "span",
"text": "a lot",
"marks": ["strong", "em", "underline"]
},
{
"_type": "span",
"text": " more",
"marks": ["strong", "em"]
}
]
This approach allows for seamless migration of rich text content with inline styles into Sanity's block content, enabling you to preserve the original formatting. For additional details, refer to the @sanity/block-tools documentation.
Sanity – build remarkable experiences at scale
Sanity Composable Content Cloud is the headless CMS that gives you (and your team) a content backend to drive websites and applications with modern tooling. It offers a real-time editing environment for content creators that’s easy to configure but designed to be customized with JavaScript and React when needed. With the hosted document store, you query content freely and easily integrate with any framework or data source to distribute and enrich content.
Sanity scales from weekend projects to enterprise needs and is used by companies like Puma, AT&T, Burger King, Tata, and Figma.