
Grab your gear: The official Sanity swag store
Read Grab your gear: The official Sanity swag storeGreat 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:
A mark is any kind of formatting or metadata you apply to text spans within Portable Text. There are two types of marks:
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.
A decorator is a simple string-based text formatting mark applied to inline text within Portable Text. Built-in decorators include:
strong)em)underline)strike-through)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.
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.
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.
Not necessarily!
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} />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!
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.
Content operations
Content backend


The only platform powering content operations
By Industry


Tecovas strengthens their customer connections
Build and Share

Grab your gear: The official Sanity swag store
Read Grab your gear: The official Sanity swag store