Handling references in portable text in GraphQL without Gatsby Source plugin
When working with Portable Text in Sanity's GraphQL API, handling references in annotations (like internal links in markDefs) requires a specific schema configuration approach.
The Key Solution: Hoist Annotations as Schema Types
The most important thing to know is that if you plan to use Sanity's GraphQL API, you should hoist your annotation types (like internalLink) as separate schema types instead of defining them inline.
Here's the difference:
❌ Inline definition (works in GROQ, problematic in GraphQL):
{
type: 'block',
marks: {
annotations: [
{
name: 'internalLink',
type: 'object', // inline object definition
title: 'Internal link',
fields: [
{
name: 'reference',
type: 'reference',
to: [{ type: 'post' }]
}
]
}
]
}
}✅ Hoisted type (works well with GraphQL):
First, create a separate schema type:
// schemas/internalLink.js
export default {
name: 'internalLink',
type: 'object',
title: 'Internal link',
fields: [
{
name: 'reference',
type: 'reference',
to: [{ type: 'post' }]
}
]
}Then reference it in your block configuration:
{
type: 'block',
marks: {
annotations: [
{
type: 'internalLink' // reference the hoisted type
}
]
}
}Why This Matters
When you hoist the annotation as a schema type, Sanity's GraphQL API can properly generate the necessary types and resolvers to handle the reference. This allows you to query the referenced document directly in your GraphQL queries, similar to how you would use the -> operator in GROQ to dereference in markDefs.
With the hoisted type, your GraphQL query can resolve the reference properly, whereas inline object definitions don't get the same treatment in the GraphQL schema generation. The GraphQL API needs explicit schema types to create the proper resolvers for nested references.
This approach is specifically mentioned in the official Sanity documentation on configuring the Portable Text Editor, which states: "If you plan to use Sanity's GraphQL API, you should hoist internalLink as a schema type, and use type: 'internalLink' as the annotation, instead of the anonymous example above."
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.