How to use references as block types in Sanity portable text

3 replies
Last updated: Oct 2, 2024
Is it not possible to set references as a block type for portable text? If is it possible, how can I join that reference in GROQ?
defineField({
	name: 'content',
	type: 'array',
	of: [
		{ type: 'block' },
		{
			type: 'reference',
			to: [{ type: 'reputation' }],
		},
	],
}),
AI Update

Yes, it is absolutely possible to set references as a block type for Portable Text! Your schema code looks correct. Let me explain how to work with it and query the referenced data.

Adding References to Portable Text

Your schema setup is valid:

defineField({
  name: 'content',
  type: 'array',
  of: [
    { type: 'block' },
    {
      type: 'reference',
      to: [{ type: 'reputation' }],
    },
  ],
})

This creates block-level references (as opposed to inline references or annotations). They'll appear as separate blocks in your Portable Text array.

Querying and Joining References in GROQ

To join (dereference) the reference in your GROQ query, you need to use the dereference operator ->:

*[_type == "yourDocType"][0] {
  content[]{
    ...,
    // For reference blocks, dereference them
    _type == "reference" => @-> {
      _id,
      _type,
      // Add the fields you need from the reputation document
      title,
      score,
      // etc.
    }
  }
}

Alternative Pattern

You can also be more explicit about handling different block types:

*[_type == "yourDocType"][0] {
  content[]{
    _type == "block" => @,
    _type == "reference" => {
      "_key": ^._key,
      "_type": "reference",
      "reputationData": @-> {
        _id,
        title,
        score
      }
    }
  }
}

Rendering in Your Frontend

When using @portabletext/react, you'll need to add a custom serializer for the reference type:

import { PortableText } from '@portabletext/react'

const components = {
  types: {
    reference: ({value}) => {
      // value will contain the dereferenced data if you queried it correctly
      return (
        <div className="reputation-reference">
          <h3>{value.title}</h3>
          <p>Score: {value.score}</p>
        </div>
      )
    }
  }
}

<PortableText value={content} components={components} />

Key Points

  1. Block-level vs Inline: Your current setup creates block-level references (separate blocks). If you want inline references within text, you'd add them to marks.annotations instead.

  2. Dereferencing: The -> operator in GROQ fetches the referenced document's data. Without it, you'll only get {_ref: "id", _type: "reference"}.

  3. Array handling: Since references are inside the content array, you need to handle them within the array projection content[]{}.

Your approach is correct! Just make sure to dereference the data in your GROQ query and add appropriate serializers in your frontend rendering.

You can, but it’s best practice to wrap your reference in an object. See the Internal Link example here . You can also see an example for how to query those references here .
Thanks for that! I'll try to wrap in an object 👍
God speed!

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?