Writing a custom serializer for @sanity/block-content-to-react
I can see you're working with @sanity/block-content-to-react, but I should mention upfront that this library is deprecated. The modern replacement is @portabletext/react, which has better support and a cleaner API.
However, to answer your specific question about lists in @sanity/block-content-to-react:
The key thing to understand is that you don't serialize <ul> or <ol> directly. Instead, you need to serialize list and listItem types. The library handles the wrapping <ul> or <ol> elements automatically based on the list style.
Here's how to properly handle lists:
import BlockContent from '@sanity/block-content-to-react'
const serializers = {
list: (props) => {
const {type} = props
const bullet = type === 'bullet'
if (bullet) {
return <ul className="custom-bullet-list">{props.children}</ul>
}
return <ol className="custom-number-list">{props.children}</ol>
},
listItem: (props) => {
return <li className="custom-list-item">{props.children}</li>
}
}
<BlockContent
blocks={content}
serializers={serializers}
/>The list serializer receives:
type: Either'bullet'or'number'children: The rendered list itemslevel: The nesting level (for nested lists)
The listItem serializer receives:
children: The content of the list item
The library automatically groups consecutive list items with the same type into a single list wrapper, which is why you serialize the list container separately from the individual items.
Migration Recommendation
If you're starting fresh or can refactor, I'd strongly recommend migrating to @portabletext/react. The syntax is cleaner:
import {PortableText} from '@portabletext/react'
const components = {
list: {
bullet: ({children}) => <ul className="custom-bullet">{children}</ul>,
number: ({children}) => <ol className="custom-number">{children}</ol>
},
listItem: ({children}) => <li className="custom-item">{children}</li>
}
<PortableText value={content} components={components} />The main differences: node becomes value, and the structure is more organized with separate categories for list, marks, types, and block.
Show original thread1 reply
Sanity – Build the way you think, not the way your CMS thinks
Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.