Writing a custom serializer for @sanity/block-content-to-react

1 replies
Last updated: Nov 27, 2020
I’m writting a custom serialiser for
@sanity/block-content-to-react'
. I can’t seem to receive <ul> or <ol> in it. This is what I have:
AI Update

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 items
  • level: 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 thread
1 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.

Was this answer helpful?