GraphQL not returning bullet and link types from Portable Text block
You're not missing anything in your schema - this is expected behavior with how Portable Text structures data!
Bullets and links aren't separate block types - they're represented differently in the data structure:
Bullets (List Items)
Bullets are still blocks with _type: 'block', but they have additional properties:
listItem: 'bullet'(or'number'for numbered lists)level: 1(for nesting level)
In GraphQL, they come through as blocks, not as a separate "bullet" type. You need to check the listItem and level fields on the block to identify them.
Links (Annotations)
Links are stored as annotations in the markDefs array, not as separate block types. The actual link text is still a span with a mark property that references the annotation. The structure looks like:
{
"_type": "block",
"children": [
{
"_type": "span",
"text": "click here",
"marks": ["abc123"] // references the markDef
}
],
"markDefs": [
{
"_key": "abc123",
"_type": "link",
"href": "https://example.com"
}
]
}How to Handle This in GraphQL
When querying GraphQL, you'll need to:
- Look for blocks with
listItemandlevelproperties to identify list items - Check the
markDefsarray on each block for link annotations - Match the
marksarray on spans to the_keyinmarkDefsto connect linked text to URLs
This is all correctly formatted in Studio because the Portable Text editor understands this structure. Your GraphQL client needs to handle the same relationships when rendering.
I'd recommend using a Portable Text renderer library for your frontend framework to handle this complexity automatically - it will properly parse listItem, markDefs, and all the other Portable Text conventions for you. Check out the guide on presenting block text for more details on rendering Portable Text.
Show original thread30 replies
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.