Key takeaway: Portable Text is a flexible, JSON-based rich text specification that allows you to author structured content once and render it in any format—from HTML and Markdown to mobile and print—without locking you into a single representation.
What Is Portable Text?
Portable Text is a language-agnostic format for representing rich text as structured JSON blocks. Unlike HTML or Markdown, it treats text as an array of content blocks, each with its own style, inline decorators, and annotations.
Core Structure
A Portable Text document is an array of block objects. Each block typically looks like this:
{
"_type": "block",
"style": "normal",
"children": [
{
"_type": "span",
"marks": ["strong"],
"text": "Example text"
}
],
"markDefs": []
}
Main Components
Blocks
Blocks are the building blocks of Portable Text. They can represent:
- Paragraphs
- Headings (h1, h2, h3, etc.)
- Blockquotes
- Lists
- Custom block types
Styles
Styles map blocks to semantic roles, similar to HTML elements:
normal→ paragraphh1,h2,h3→ headingsblockquote→ quotescode→ code blocks
Decorators
Decorators apply inline formatting within spans:
strong→ boldem→ italicunderline→ underlinecode→ inline code
Annotations
Annotations attach rich metadata to text spans, such as links or custom objects:
- External links (URL)
- Internal cross-references
- Custom annotations
Configuring Portable Text in Sanity
Basic Schema
The simplest configuration for a Portable Text field:
export default {
name: 'content',
type: 'array',
title: 'Content',
of: [{ type: 'block' }]
}
Advanced Schema
Customize styles, decorators, and annotations:
export default {
name: 'content',
type: 'array',
title: 'Content',
of: [
{
type: 'block',
styles: [
{ title: 'Normal', value: 'normal' },
{ title: 'Heading 1', value: 'h1' },
{ title: 'Heading 2', value: 'h2' },
{ title: 'Quote', value: 'blockquote' }
],
marks: {
decorators: [
{ title: 'Bold', value: 'strong' },
{ title: 'Italic', value: 'em' }
],
annotations: [
{
title: 'Link',
name: 'link',
type: 'object',
fields: [{ title: 'URL', name: 'href', type: 'url' }]
}
]
}
}
]
}
Adding Custom Blocks
You can include images, code blocks, and other custom types:
export default {
name: 'content',
type: 'array',
title: 'Content',
of: [
{ type: 'block' },
{ type: 'image' },
{ type: 'code' }
]
}
Rendering Portable Text
Sanity offers official rendering libraries for multiple frameworks:
- React:
@portabletext/react - Vue:
@portabletext/vue - Svelte:
@portabletext/svelte - React Native:
@portabletext/react-native - HTML:
@portabletext/to-html
React Rendering Example
import { PortableText } from '@portabletext/react';
const components = {
types: {
image: ({ value }) => <img src={value.imageUrl} alt={value.alt} />,
code: ({ value }) => (
<pre>
<code>{value.code}</code>
</pre>
)
},
marks: {
link: ({ children, value }) => <a href={value.href}>{children}</a>
}
};
function MyComponent({ content }) {
return <PortableText value={content} components={components} />;
}
Advantages of Portable Text
Flexibility and Portability
Content authored once can be output to web, mobile, PDF, or any custom format without rewriting.
Structured Data
JSON blocks make querying and transforming content easy with GROQ or standard JSON tools.
Extensibility
Define custom block types, styles, and annotations tailored to your project’s needs.
Validation and Consistency
Sanity schemas let you enforce content rules and maintain consistency across documents.
Use Cases
- Blogs and Articles: Rich formatting, embedded images, and links.
- Documentation: Code blocks, multi-level headings, and cross-references.
- Omnichannel Content: Serve the same content to websites, mobile apps, and PDFs without loss of structure.