How to serialize span (plain text) in Sanity PortableText serializer?

13 replies
Last updated: Jan 28, 2026
Hi again, trying to write a serializer and can't figure out how to handle pure text.It looks like pure text is of type "span" from my console.logging, but I can't get my serialier to actually do anything with span.


const serializer = {
  types: {
    figure: ({ value }) => <Image data={value} />,
    span: ({ value }) => <Text fontSize="8em">..</Text>,
    // Headings are also not working ):
    block(props) {
      switch (props.node.style) {
        case "h1":
          return <Text fontSize="8em">..</Text>;
        case "h2":
          return <Text fontSize="8em">..</Text>;
        case "span":
          return (
            <Text fontSize="12em">
              Should span be here instead? Still not working though
            </Text>
          );
      }
    },
  },

const Content = (props) => {
  return <PortableText value={props.value} components={serializer} />;
};
};
Any suggestions?
AI Update

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:

  1. Remove the span from types - it's not a valid type to override there
  2. Use props.children in your block handler - this renders the actual text content
  3. Handle the "normal" style - plain paragraphs have style: "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 thread
13 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.

Was this answer helpful?