How to query and render Sanity rich text/Portable Text as HTML?

5 replies
Last updated: May 18, 2022
Hi,
could you help me figure out how to use rich text from sanity?

Can I not query data from the field as HTML?

I can only see stuff like:

{
"content": [
    {
    "children": [
        {
        "text": Some bold text
        }
        {
        "text": some normal text.
        }
If I make some text bold, it becomes a child.

What am I missing?

Thanks!
AI Update

You're not missing anything! What you're seeing is exactly how Sanity's rich text works - it's called Portable Text, and it's a JSON-based format rather than HTML.

The structure you're seeing is intentional: bold text creates separate objects because marks (like bold, italic, etc.) are stored as metadata on text spans. This is why formatting changes create new "children" in the structure.

You cannot query Portable Text directly as HTML because it's stored as structured JSON data in Sanity's Content Lake. However, you have several options to convert it to HTML for display:

Rendering Options

1. Use @portabletext/react (React apps)

The modern, recommended approach:

import {PortableText} from '@portabletext/react'

<PortableText value={yourContent} />

2. Use @portabletext/to-html (Node.js/server-side)

For converting to HTML strings:

import {toHTML} from '@portabletext/to-html'
const html = toHTML(yourContent)

3. Use pt::text in GROQ (plain text only)

If you only need plain text without formatting, you can query:

*[_type == "post"] {
  "plainText": pt::text(content)
}

Note: This strips all formatting and just gives you the raw text.

Why Not HTML?

Portable Text's JSON structure actually gives you several advantages over storing HTML:

  • Security: No XSS vulnerabilities
  • Platform-agnostic: Render the same content on web, mobile, email, voice assistants, etc.
  • Customizable: You can create custom serializers to render content exactly how you want
  • Structured: Content is semantic and queryable

The serializers handle transforming that JSON structure into whatever format you need. You define how each block type, mark, and style should render in your specific application.

Note: The older block-content-to-html package is deprecated, so stick with the @portabletext/* packages for new projects.

Show original thread
5 replies
The rich text data doesn’t yield HTML, it yields an AST (Abstract Syntax Tree) so it can eventually be rendered in any sort of format (HTML, JSX, etc.).
So you need a renderer. If you use React, then you should use the library you just linked. If you want HTML, you want https://github.com/portabletext/to-html .
There is a list of renderers here: https://www.sanity.io/docs/presenting-block-text
Thank you!

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?