Creating a field in Sanity for a blog and generating an excerpt, with discussion on previewing references and using a renderer component.

15 replies
Last updated: Apr 4, 2022
Hello there,I'm using Sanity for a blog, and I wonder if there's some way to create a field that gets populated with the text from another field (a blockContent).
Initial thought:

{

name: "excerpt",

title: "Excerpt",

type: "string",

options: {

source: "GET THE BLOCK CONTENT",

maxLength: 96,

},
Or can an excerpt get generated for the blockContent in an easier way?
(Sometimes the blogposts starts with a picture and sometimes with text, so I don't want to get the data by doing something static like
post.body[0]
AI Update

You definitely can auto-generate an excerpt from your blockContent field! The best approach is using the pt::text() function in your GROQ queries to extract plain text from Portable Text, which handles images, text blocks, and any other content automatically.

Instead of storing the excerpt as a separate field, generate it when you query your posts:

*[_type == "post"] {
  title,
  body,
  "excerpt": pt::text(body)[0..96]
}

The pt::text() function extracts all plain text from your Portable Text blocks, automatically handling whether your post starts with an image or text—exactly what you need! Then you can use array slicing ([0..96]) to limit it to the first 96 characters.

For a word-based excerpt instead of character-based, you can combine pt::text() with the string::split() function:

*[_type == "post"] {
  title,
  body,
  "excerpt": array::join(string::split(pt::text(body), " ")[0..20], " ")
}

This takes the first 20 words instead of cutting mid-word. Here's what's happening:

  1. pt::text(body) extracts plain text from your Portable Text
  2. string::split(pt::text(body), " ") splits it into an array of words
  3. [0..20] takes the first 20 words
  4. array::join(..., " ") joins them back into a string with spaces

Schema-level initialValue (Alternative)

If you really want a stored excerpt field in your schema, you can use initialValue:

{
  name: "excerpt",
  title: "Excerpt",
  type: "string",
  initialValue: async ({document, getClient}) => {
    if (!document?.body) return '';
    
    const client = getClient({apiVersion: '2023-01-01'});
    const result = await client.fetch(
      `*[_id == $id][0]{"text": pt::text(body)}`,
      {id: document._id}
    );
    
    return result?.text?.substring(0, 96) || '';
  }
}

However, this approach is more complex and has downsides—the excerpt won't update automatically when the body changes, and you'd need to manually re-save documents to refresh it.

Why Query-time is Better

The query-time approach using pt::text() is simpler and more maintainable because:

  • It always reflects the current content (no sync issues)
  • Handles images, embeds, and text blocks automatically—no need to worry about post.body[0] being an image
  • No need to update the excerpt when body changes
  • Works with GROQ's string and array manipulation functions for flexible text processing

The pt::text() function is specifically designed for this use case—extracting searchable, readable text from rich Portable Text content for things like excerpts, search indexing, and SEO meta descriptions.

yeah, there are other ways. Were do you want to excerpt to appear (front end or as a preview within the sanity studio?
user M
Do you have examples for how to do a preview in sanity studio?
user E
Yeah I do. But can you tell me a bit more: Where do you want to preview the exerpt? I have quite a lot of examples and not a lot of time at the moment, so this will help me find the right one for you :)
The simplest cases is where I'd like to show incoming references.• In a folder, I have
_ref
to a parent folder, but no obvious solution to list the children. Children being other folders with this
_id
in the parent field.• Similarly, a list and links to all articles that have
_ref
to this article.I've managed to produce a desk structure that gives me the overview I need but it would be more useful in the document view it self.

Thanks for taking the time
user M
.
Now I think I know what you need and will find that for you later …
user M
Sorry to put a ⏲️ on you but you got me excited and curious to see some examples 😄
hi
user E
So i FINALLY found my code for it.
import { BsTypeH1 } from "react-icons/bs";
import React from 'react'

const Preview = ({ value }) => {
    const { headline, subline, body } = value;

//This is just so I dont have to use types in this example, In my code I have a preview component in src-folder
    let subline2;
    subline ? subline2 = subline.slice(0, 20) : subline2 = null;

    let body2;
    body ? body2 = body.slice(0, 90) : body2 = null;


    return (
        <div style={{ display: 'flex', alignItems: 'center' }}>

            <BsTypeH1 style={{ verticalAlign: 'middle', fontSize: '1.5em', padding: '0 10px' }} />


            <p>
                Text Header Section: <strong>{headline}</strong> <br />
                <span>{subline2}</span><br />
                {body && <small>{body2}...</small>}
            </p>
        </div>
    )
}


export default {
    name: 'headerSection',
    title: 'Text Header Section',
    type: 'object',
    icon: BsTypeH1,
    fields: [

        {
            name: 'headline',
            type: 'string',
            rows: 2,
        },
        {
            name: 'subline',
            title: 'Subline',
            type: 'text',
            rows: 1,
        },
        {
            name: 'body',
            title: 'Body',
            type: 'richText',
        },
    ],
    preview: {
        select: {
            headline: 'headline',
            subline: 'subline',
            body: 'body',
            media: 'icon'
        },
        component: Preview

    }
}
Using the `.slice`method worked great for me INSIDE the studio
outside will follow, I am on the search at the momentz
Ok, I think I see what you do here.. And this is just manipulating the preview of a document from the "outside". Like when you se a list of documents in studio. (?)
AH and now I found an example for a preview of a reference inside the studio:1. The structure in my richText (blockContent) which references a job and then I setup the preview (renderer) component
2. Renderer job
user E
i don’t get your question 🙈Wait I’ll post another pic, so it is more clear
So in order to be able to display or preview any schema, you can do that INSIDE the schema you want to preview (reference) later -&gt; then you can be sure, that it will be displayed right everywhere.
Yep! Nice. I'll try this out. Thanks

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?