Troubleshooting serializer for text and headings in PortableText component
Looking at your code, I can see the issue! In Portable Text, span isn't actually a type you can customize in the types object. Spans are the text content within blocks, and they're handled differently.
Here's what's happening in your Portable Text structure:
- Blocks (like paragraphs, headings) are handled in
types.block - Spans are the actual text content inside those blocks
- Marks (like bold, italic, or custom annotations) are handled in
marks
To customize how text renders, you need to target the block type. Your block handler is on the right track, but you need to return something for the default "normal" style too:
const serializer = {
types: {
figure: ({ value }) => <Image data={value} />,
block(props) {
switch (props.node.style) {
case "h1":
return <Text fontSize="8em">{props.children}</Text>;
case "h2":
return <Text fontSize="6em">{props.children}</Text>;
case "normal": // This is the default paragraph style
return <Text fontSize="2em">{props.children}</Text>;
default:
return <Text>{props.children}</Text>;
}
},
},
};The key things to fix:
- Remove the
spanfromtypes- it's not a valid type to override there - Use
props.childrenin your block handler - this renders the actual text content - Handle the
"normal"style - plain paragraphs havestyle: "normal", not"span"
If you want to customize individual text spans (like making specific words different), you'd use the marks object instead. For example, if you had a custom annotation or wanted to override how bold text renders:
const serializer = {
types: { /* your block handlers */ },
marks: {
strong: ({ children }) => <Text fontWeight="bold">{children}</Text>,
// custom marks would go here too
},
};The ultimate guide for customizing Portable Text has great examples of all the different ways you can customize rendering, including blocks, marks, and annotations!
Show original thread13 replies
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.