Explainer Needed: What is a Mark, Decorator and Serializer?

2 replies
Last updated: Apr 2, 2021
Hello, I don't understand the following concepts? Can someone please explain?
1. What is a mark/custom mark?
2. What is a decorator? Where is a decorator used and what is it's purpose?
3. What is the difference between a mark and a decorator?
4. What is a serializer? What is the purpose of a serializer?
5. Do I have to write a serializer everytime I add a new decorator? If yes, then how do I write that serializer?
6. Let's say I have a component called OnePost.js where I render a single blog post. In the blog post body I have a code block and also some highlighted text. As far as I understand, I have to write two serializers? How do I write two serializers?
AI Update

Great questions! These concepts can be confusing at first, but they're all part of how Portable Text works in Sanity. Let me break each one down:

1. What is a mark/custom mark?

A mark is any kind of formatting or metadata you apply to text spans within Portable Text. There are two types of marks:

  • Decorators: Simple text formatting (bold, italic, underline, etc.)
  • Annotations: Complex marks with additional data (links, footnotes, citations, etc.)

A custom mark is any mark you define yourself beyond the built-in ones. For example, you might create a custom decorator for highlighting text or a custom annotation for internal document references.

2. What is a decorator? Where is it used and what's its purpose?

A decorator is a simple string-based text formatting mark applied to inline text within Portable Text. Built-in decorators include:

  • Bold (strong)
  • Italic (em)
  • Underline (underline)
  • Strikethrough (strike-through)
  • Code (code)

Purpose: Decorators provide straightforward text styling within your rich text content. They're used in the Portable Text Editor when content editors need to format text.

3. What's the difference between a mark and a decorator?

  • Mark is the umbrella term for any formatting applied to text
  • Decorator is a specific type of mark that's simple and boolean (on/off)
  • Annotation is the other type of mark that can carry complex data structures

Think of it like this: All decorators are marks, but not all marks are decorators. Annotations are marks that need additional data (like a URL for a link), while decorators just toggle formatting on or off.

4. What is a serializer? What's its purpose?

A serializer (now more commonly called a "component" in modern libraries) is a function that tells your frontend how to render Portable Text content. Since Portable Text is stored as JSON, you need serializers to transform that JSON into HTML, React components, or whatever format your frontend needs.

Purpose: Serializers bridge the gap between Sanity's structured content and your website's display layer.

5. Do I need to write a serializer for every new decorator?

Not necessarily!

  • Built-in decorators (bold, italic, etc.) already have default serializers in libraries like @portabletext/react
  • Custom decorators do need custom serializers to tell the frontend how to render them

Here's how to write a serializer for a custom decorator (like subscript):

import {PortableText} from '@portabletext/react'

const components = {
  marks: {
    sub: ({children}) => <sub>{children}</sub>,
    sup: ({children}) => <sup>{children}</sup>
  }
}

<PortableText value={portableTextContent} components={components} />

6. Writing multiple serializers (code block + highlighted text)

You don't write two separate serializers—you write one serializer configuration object that handles both! Here's an example:

import {PortableText} from '@portabletext/react'

const components = {
  types: {
    // For custom block types like code blocks
    code: ({value}) => (
      <pre>
        <code className={`language-${value.language}`}>
          {value.code}
        </code>
      </pre>
    )
  },
  marks: {
    // For inline marks like highlighting
    highlight: ({children}) => (
      <span className="bg-yellow-200">{children}</span>
    )
  }
}

// In your OnePost.js component:
function OnePost({post}) {
  return (
    <article>
      <h1>{post.title}</h1>
      <PortableText 
        value={post.body} 
        components={components} 
      />
    </article>
  )
}

The components object handles all your custom rendering in one place:

  • types handles custom block-level elements (like code blocks, images, etc.)
  • marks handles inline formatting (like your highlighted text)

Check out this comprehensive guide on customizing Portable Text for more examples!

Of course!
1.
mark/custom mark is how you are definitely a custom decorator (see what a decorator is in #2). This usually is adding an emphasis, or strong to your text. So making it either italic or bold). They can also be made custom. See more on that in #2. 2. A decorator is styling your text. For example: making it
strong. Taking it a step further, you can customize those decorators and style it to look like code or highlighted. Be sure to look at the example code here under Decorators.3. The difference between the two is a mark is any type of decorator or functionality you can give to text. A decorator is defining that style you want to give in the mark. I’m going to walk through the code
here under Properties. If you look, you’ll see “cbe9d12c6af9” under children --&gt; in the 3rd span. If you scroll down, you’ll see
markDefs
. And you’ll see “cbe9d12c6af9" again. Those are linking the span and the markDefs together. And then the markDefs has an
href
with a link. So now that span linking to that markDefs with the link makes that text in the span, clickable, and linking to that particular link. The text that is linked is “annotated” and you’ll see that all within the 3rd span.4. A serializer is where the content is pulled from lots of “blocks” and make into a more readable format. For example. In that code we were just looking over, we have 4 spans with 4 “text” spots. So those 4 blocks of content are smashed together to make it readable for the user. So those four would say this: “I am strong and annotated” with a bold on strong and a link on annotated.
5. The decorator is for the span, the serializer takes all the spans and makes them one. So no, only one serializer for your content. But depending on how you want your content “serialized” is which package you use. For example,
block-content-to-react
is the Serializer for taking your blocks of spans, making them readable, and pushing to a React frontend. You’ll see the helpers here for different ways to serializes your data.6. You’ll only need one serializer for that. If all the content (text, images, highlighted text, and code block) all live in the same rich text editor. The rich text editor is where the blog post content would live, meaning the code blocks and such.
After writing all these answers down, I have decided I’m going to write a blog post on this content. I’m going to take these questions you asked, write it up, and share it with the community. That way, others who have these same questions can find the answers. We really appreciate you asking! I will also make sure to share the blog post with you. I will have specific code blocks and examples in it. That will help even further in understanding.

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?