Render Portable Text

is a standardized data format for rendering rich text content. Most content management systems will output raw HTML or Markdown data, but Sanity will output Portable Text, giving you a data structure that allows for full control of content rendering.

In this guide, we'll walk through the steps to render Portable Text in our Astro site.

Inspect Source

In order to understand how to render Portable Text, it might be helpful to see some raw data. You can load up Sanity Studio in your local development environment and switch to the "Vision" tab. From this tab, you can query data using .

The Portable Text we're interested in will (almost always) be the body attribute on our document.

Let's grab our first post and its body in a query:

*[_type == "post" && isVisible == true][0] {

Your data may look different, but here's a small sample of the output:

Sanity Vision

The body element is structured JSON that we can iterate through to render rich content like paragraphs, bullet lists, images, and anything else we might need for a website.

The Portable Text Standard provides details on how this data is structured. We won't go into details in this guide, but it will be a valuable resource for when you want to write your own block renderers.

Let's jump ahead to use a resource from someone who has already read the standard.

<PortableText> Component

Space Madness has a convenient PortableText component that is ready for your use with no other configuration necessary. It uses the library, and includes custom components to render blocks and marks.

It's dead simple to use:

{post.body && <PortableText value={post.body} />}

But what is going on under the hood?

If you take a look at the site-astro/src/components/PortableText directory you will see a list of components that map to each type of content to render.

├── Block.astro
├── List.astro
├── ListItem.astro
├── Mark.astro
├── PortableText.astro
├── Type.astro
└── custom
    ├── Callout.astro
    ├── CodeBlock.astro
    ├── Embed.astro
    ├── Figure.astro
    ├── Image.astro
    ├── InternalLink.astro
    └── PartialInclude.astro

We'll go over a few of the basic types of content in a Portable Text object.

Blocks are the basic content section of the data. Think of paragraphs, sections, headings. etc. They can have children that are also blocks.

Each block can be further split by its Type. Types include the custom blocks like image, callout, and code blocks.

Marks are content that are "inline", like spans, strong, emphasis, and highlights. For Space Madness, the "Internal Link" mark is how our content popovers are rendered.

Lists are numbered or bulleted sections with list item children.

List Items are the elements rendering inside ordered and unordered lists.

This should demystify some of the internals that make Space Madness work. Space Madness comes with several examples of custom Type and Mark renderers. You can use these as a template for your own components.