Portable Text definition
Portable Text is an open specification for storing rich text as structured JSON: an array of typed blocks whose text, styles, annotations, and custom content types are held as data, so the same content can be rendered into HTML, React, Markdown, PDF, or any other output format without being locked to one of them.
Portable Text is an open specification that stores rich text as JSON instead of as a markup string, so a paragraph, its links, and any embedded images or code blocks exist as

How is Portable Text structured?
A Portable Text document is a JSON array of blocks, and each block carries a `_type` that tells a renderer what it is. Standard text blocks use `_type: "block"` and add a style such as `normal`, `h1`, or `blockquote`, a `children` array of inline span objects that hold the text, and a `markDefs` array that defines annotations such as links. Lists are handled with optional `listItem` and `level` properties, so nesting is expressed as data, not as nested tags.
Spans carry a `marks` array whose entries are one of two things: a decorator such as `strong` or `em`, which is a simple on or off flag, or a key pointing at an entry in `markDefs`. The specification separates the two because annotations carry data (a link has an `href`, and a reference has a target), and keeping the definition separate from the reference "means multiple spans can share the same annotation data without duplication."
Any block whose `_type` is something else is a custom block: an image, a code sample, an embedded video, or a call to action, holding whatever fields you define for it. Renderers that do not recognize a custom block type skip it. Every block also gets a `_key`, which the spec explains exists for collaboration: "When two editors modify the same document, keys let the system identify which block changed without relying on array position."
Why use Portable Text instead of HTML or Markdown?
Portable Text is used instead of HTML or Markdown because HTML and Markdown are output formats that bake rendering decisions into the content, not structural representations that you serialize at render time. As the specification puts it, "HTML and Markdown encode rendering assumptions into the content. Portable Text separates content structure from presentation."
The difference is easiest to see in a sentence with a link. In HTML, that sentence is `<p>Read the <a href="/docs">documentation</a> for <strong>Portable Text</strong>.</p>`. The anchor tag is an instruction to a browser, so anything that is not a browser has to parse the string and infer structure. In Portable Text, the same sentence is one block containing four spans, a `strong` decorator on one of them, and a `markDefs` entry of `_type: "link"` with `href: "/docs"`. The link is data, which is why it can become an `<a>` tag on a web page, a `<Link>` component in a React app, a footnote in a PDF, or a bare URL in a plain text export, all from the same stored content.
That also means the array itself is queryable. The spec notes that the array-of-blocks structure "makes it natural to insert, reorder, and query blocks," which is difficult to do reliably against a single escaped HTML string.
How do you render Portable Text?
You render Portable Text by passing the JSON array to a serializer for your target output and supplying a map of components that says how each block type should appear. In React, that is the `@portabletext/react` package, where a `components` map controls how custom types such as an image block or a code block render. Sanity's documentation notes that the older `@sanity/block-content-to-react` package was deprecated in 2022 in favor of `@portabletext/react`.
Official renderers exist for React, HTML, Vue, Svelte, and Astro, plus SolidJS, React Native, and React PDF, according to the specification page. Beyond JavaScript, there are independent implementations in C#/.NET, Python, PHP, Ruby, Go, and Dart/Flutter, and platform-level support in
One Sanity-specific detail worth knowing: because annotations are data, a `markDefs` entry can hold a reference to another document, and a GROQ projection resolves it inside the array at query time, so an internal link arrives at your frontend with the target document's current slug already attached.
What is the difference between Portable Text and the Portable Text Editor?
Portable Text is the data format, not the Portable Text Editor, which is one editor implementation that produces it. They are often treated as the same thing, but the format is specified independently, and the specification states that it is "not tied to any specific CMS or editor." The Portable Text Editor is described on portabletext.org as "a fully customizable block content editor you can embed in any React application," and it is not the only editor producing the format: EmDash, built on TipTap, is listed as a separate implementation.
Portable Text is also sometimes confused with block content or the older term block text. Block content is the general idea of rich text composed of discrete typed blocks. Portable Text is a specific open specification for encoding that idea. Sanity's documentation explains the naming directly: "You may notice some mentions of block text, including in the tool names. This was the nomenclature we used before open sourcing and publishing the specification for Portable Text."
A further distinction: editors such as ProseMirror, Slate, and Lexical also use
Why does Portable Text matter for AI and multi-channel content?
Portable Text matters for AI and multi-channel delivery because structured blocks are addressable and chunkable in a way that an HTML string is not. A language model, a search index, or a retrieval pipeline consuming an HTML blob has to strip tags and infer where one idea ends and the next begins. Consuming Portable Text, it gets a sequence of typed blocks it can iterate over, filter by style, or split on, with images and embedded components clearly marked, not buried in markup.
The same property makes a single rich text field usable across channels. Because presentation is applied at render time, not stored, one body field can serve a website, a native app, an email, a PDF, and an agent that needs the text without any of the styling. Sanity, the Content Operating System for the AI era, stores rich text this way by default, so content modeled once in the Studio is queryable as data and renderable anywhere rather than requiring a separate copy per destination.
The format has been stable in practice since 2018 and is used in production by Sanity, EmDash, Hugo, and others. The specification is published at version 0.0.1 as a working draft, which, in the spec's own words, "reflects that the formal specification document is still being refined, not that the format is unstable." The spec is developed in the open on GitHub.
Unlock New Possibilities with Sanity
With Portable Text under your belt, it's time to see what Sanity can do for you. Explore our features and tools to take your content to the next level.
Last updated: