Best way to query GROQ for blog body with list, marks, and formatting?

10 replies
Last updated: Oct 19, 2021
What is the best way to GROQ out
Body
for the blog, like [list, mark, bold text, etc]? Do i have to
body.map(b => b.children
or there is easy way?
AI Update

When querying Portable Text content in GROQ, you typically don't need to manually map through body.children - you just query the field normally and handle the rendering in your frontend code. Let me explain the best approaches:

Querying Portable Text in GROQ

In your GROQ query, you can simply include the body field as-is:

*[_type == "post"] {
  title,
  body
}

This returns the complete Portable Text structure (blocks, marks, annotations, etc.) which you then render in your frontend.

Rendering the Content

The easiest way to handle Portable Text is to use Sanity's official rendering libraries for your framework:

For React/Next.js:

import {PortableText} from '@portabletext/react'

<PortableText value={post.body} />

For other frameworks:

These libraries automatically handle all the block types (paragraphs, lists, marks like bold/italic, etc.) without you needing to manually iterate through children.

If You Need Plain Text

If you actually need to extract just the plain text (for search, previews, or meta descriptions), use the pt::text() function directly in your GROQ query:

*[_type == "post"] {
  title,
  "plainText": pt::text(body)
}

This strips all formatting and gives you clean text without any manual mapping.

Custom Rendering

If you need custom styling for specific marks or blocks, you can pass custom components to the PortableText renderer:

<PortableText 
  value={post.body}
  components={{
    marks: {
      strong: ({children}) => <strong className="font-bold">{children}</strong>
    },
    block: {
      h1: ({children}) => <h1 className="text-4xl">{children}</h1>
    }
  }}
/>

Bottom line: Use the official Portable Text rendering libraries rather than manually mapping through the structure. They handle all the complexity for you!

Show original thread
10 replies
Will
npm install --save @sanity/block-content-to-react
work with nextjs as well?
Will
npm install --save @sanity/block-content-to-react
work with nextjs as well?
If you're using Next, I recommend using next-sanity . It's a great toolkit and has some good examples of usage in its readme.
Great! it did work with line of code
import SanityPortableText from '@sanity/block-content-to-react';
though i wonder what is
usePreviewSubscription
? how to do i make link as pure html like blue color.
Use Preview Subscription will be for if you set up live previews of your content inside of the sanity Studio. To make an
<a>
tag, you would have to set up a serializer for the block. How do you currently have it set up in your Portable Text editor?
It look something like that:// schema.js

export default {
  name: 'body',
  type: 'array',
  title: 'Body',
  of: [
    {
      type: 'block'
    }
  ]
}
// blockContent.js

/**
 * This is the schema definition for the rich text fields used for
 * for this blog studio. When you import it in schemas.js it can be
 * reused in other parts of the studio with:
 *  {
 *    name: 'someName',
 *    title: 'Some title',
 *    type: 'blockContent'
 *  }
 */
export default {
  title: 'Block Content',
  name: 'blockContent',
  type: 'array',
  of: [
    {
      title: 'Block',
      type: 'block',
      // Styles let you set what your user can mark up blocks with. These
      // correspond with HTML tags, but you can set any title or value
      // you want and decide how you want to deal with it where you want to
      // use your content.
      styles: [
        { title: 'Normal', value: 'normal' },
        { title: 'H1', value: 'h1' },
        { title: 'H2', value: 'h2' },
        { title: 'H3', value: 'h3' },
        { title: 'H4', value: 'h4' },
        { title: 'Quote', value: 'blockquote' },
      ],
      lists: [{ title: 'Bullet', value: 'bullet' }],
      // Marks let you mark up inline text in the block editor.
      marks: {
        // Decorators usually describe a single property – e.g. a typographic
        // preference or highlighting by editors.
        decorators: [
          { title: 'Strong', value: 'strong' },
          { title: 'Emphasis', value: 'em' },
        ],
        // Annotations can be any object structure – e.g. a link or a footnote.
        annotations: [
          {
            title: 'URL',
            name: 'link',
            type: 'object',
            fields: [
              {
                title: 'URL',
                name: 'href',
                type: 'url',
              },
            ],
          },
        ],
      },
    },
    // You can add additional types here. Note that you can't use
    // primitive types such as 'string' and 'number' in the same array
    // as a block type.
    {
      type: 'image',
      options: { hotspot: true },
    },
  ],
}
It look something like that:// schema.js

export default {
  name: 'body',
  type: 'array',
  title: 'Body',
  of: [
    {
      type: 'block'
    }
  ]
}
// blockContent.js

/**
 * This is the schema definition for the rich text fields used for
 * for this blog studio. When you import it in schemas.js it can be
 * reused in other parts of the studio with:
 *  {
 *    name: 'someName',
 *    title: 'Some title',
 *    type: 'blockContent'
 *  }
 */
export default {
  title: 'Block Content',
  name: 'blockContent',
  type: 'array',
  of: [
    {
      title: 'Block',
      type: 'block',
      // Styles let you set what your user can mark up blocks with. These
      // correspond with HTML tags, but you can set any title or value
      // you want and decide how you want to deal with it where you want to
      // use your content.
      styles: [
        { title: 'Normal', value: 'normal' },
        { title: 'H1', value: 'h1' },
        { title: 'H2', value: 'h2' },
        { title: 'H3', value: 'h3' },
        { title: 'H4', value: 'h4' },
        { title: 'Quote', value: 'blockquote' },
      ],
      lists: [{ title: 'Bullet', value: 'bullet' }],
      // Marks let you mark up inline text in the block editor.
      marks: {
        // Decorators usually describe a single property – e.g. a typographic
        // preference or highlighting by editors.
        decorators: [
          { title: 'Strong', value: 'strong' },
          { title: 'Emphasis', value: 'em' },
        ],
        // Annotations can be any object structure – e.g. a link or a footnote.
        annotations: [
          {
            title: 'URL',
            name: 'link',
            type: 'object',
            fields: [
              {
                title: 'URL',
                name: 'href',
                type: 'url',
              },
            ],
          },
        ],
      },
    },
    // You can add additional types here. Note that you can't use
    // primitive types such as 'string' and 'number' in the same array
    // as a block type.
    {
      type: 'image',
      options: { hotspot: true },
    },
  ],
}
Got it! This guide (specifically the part about rendering the links with React ) will show you which serializer you need to set up.
Hi Racheal! i have little problem here, when i select few line that i want to be
h1
as you seen in the screenshot, some other text also get effected by and bigger as
h1
, i only want the selected text to be
h1
any clue why? 🙂 only the bold text is working.
And he
H1
text is not
h1
in my frontend.

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?