Issue with serializers in 11TY + Sanity integration
I can see you're working with nested Portable Text in a custom block type using @sanity/block-content-to-html in your 11ty project. The issue is that bodyInfo contains an array of Portable Text blocks, not simple text, so you need to handle it differently.
First, it's important to note that @sanity/block-content-to-html is deprecated and the Sanity team recommends migrating to @portabletext/to-html. However, I'll help you solve this with your current setup.
The problem with your infoText serializer is that node.bodyInfo is an array of Portable Text blocks (you can see in your data it has the full block structure with _type: "block", children, etc.), so you can't just access .children directly.
Here's how to fix it with @sanity/block-content-to-html:
const blocksToHtml = require('@sanity/block-content-to-html');
const { h } = require("@sanity/block-content-to-html");
module.exports = {
types: {
cta: ({ node }) => {
return h(
'a',
{
className: 'bg-yellow-500 text-white',
href: node.ctaUrl,
},
node.ctaText,
)
},
infoText: ({ node }) => {
// Render the nested Portable Text by calling blocksToHtml
const innerHtml = blocksToHtml({
blocks: node.bodyInfo,
// You can pass the same serializers recursively if needed
serializers: module.exports
});
return h(
'div',
{
className: 'bg-blue-500 text-white',
},
innerHtml
)
},
},
}Key points:
node.bodyInfois an array of blocks, not just children. Looking at your data structure, it contains[{ _type: "block", children: [...], markDefs: [], style: "normal" }]You need to call
blocksToHtml()recursively to serialize the nested Portable Text contentI changed the wrapper from
<p>to<div>because you're rendering block-level content inside it (paragraphs inside paragraphs isn't valid HTML)You can pass
serializers: module.exportsto ensure any custom serializers you've defined also apply to the nested content
Alternative: Extract plain text only
If you only want the plain text without any HTML structure from the nested content:
infoText: ({ node }) => {
const plainText = node.bodyInfo
.map(block =>
block.children
?.map(child => child.text)
.join('')
)
.join(' ');
return h(
'p',
{ className: 'bg-blue-500 text-white' },
plainText
)
}This extracts just the text content ("test test info" in your example) without rendering it as HTML blocks.
Recommendation for the future:
Since @sanity/block-content-to-html is deprecated, consider migrating to @portabletext/to-html when you have time. The new package has better TypeScript support and a more modern API, though the concepts are similar.
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.