How to get referenced document attributes in a custom block serializer

27 replies
Last updated: Sep 9, 2021
Simple question but looks tricky: I created a custom Block in the Content Editor and used a “Reference” type for this (referencing to a Video schema which interacts with Mux). So i can easily pick an already uploaded video to appear right inside my blog. Problem is, in my serializer for this custom type, i only get back a _ref pointing to a document and not the needed infos like playbackId and other related things i get when i query the referred document itself. Is there an easy way to get back the referenced doc attributes from inside my render function?
AI Update

The issue you're experiencing is a common one when working with references in Portable Text. When you query Portable Text that contains custom blocks with references, you only get the _ref ID, not the actual document data. The solution is to dereference the reference in your GROQ query using special syntax for nested arrays.

Since your reference is inside a custom block within your Portable Text array, you need to use GROQ's dereferencing operator (->) combined with array projection syntax.

Solution: Dereference in Your GROQ Query

In your GROQ query where you fetch the Portable Text content, you need to explicitly dereference the reference:

*[_type == "post"][0] {
  title,
  content[]{
    ...,
    // For your custom video block
    _type == "videoBlock" => {
      ...,
      "video": videoReference->{
        _id,
        title,
        "playbackId": asset->playbackId,
        "assetId": asset->assetId,
        // any other Mux fields you need
      }
    }
  }
}

The key parts here are:

  • videoReference-> - The -> operator dereferences the reference and fetches the referenced document
  • You can chain dereferences, like asset->playbackId to get nested reference data
  • Wrap this in a conditional (_type == "videoBlock" =>) to only apply it to your specific block type

For References in Annotations (markDefs)

If your video reference is actually an annotation rather than a block, the syntax is slightly different because it's nested inside the markDefs array:

content[]{
  ...,
  markDefs[]{
    ...,
    _type == "videoReference" => {
      ...,
      "videoData": @.reference->{
        _id,
        title,
        "playbackId": asset->playbackId,
        "assetId": asset->assetId
      }
    }
  }
}

The @ symbol refers to the current item in the array context, which is necessary when working with nested arrays like markDefs.

In Your Serializer

Once you've dereferenced in the query, your serializer will receive the full data:

import { PortableText } from '@portabletext/react'

const components = {
  types: {
    videoBlock: ({value}) => {
      // Now value.video contains all the fields you queried
      const { playbackId, title } = value.video
      
      return (
        <div>
          <h3>{title}</h3>
          <MuxPlayer playbackId={playbackId} />
        </div>
      )
    }
  }
}

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

This approach ensures you get all the referenced document data in a single query, avoiding the need to fetch it separately in your render function. The dereferencing happens at query time, so your serializer receives complete data objects rather than just reference IDs.

// videoEmb.js
export default {
  name: 'videoEmb',
  type: 'object',
  title: 'Video',
  fields: [
    {
      name: 'video',
      title: 'Video',
      type: 'reference',
      to: {type: 'videos'},
    }
  ]
}

export default {
  title: 'Block Content',
  name: 'blockContent',
  type: 'array',
  of: [
    {
      title: 'Block',
      type: 'block',
      ....
     },
     {
      type: 'videoEmb',
     }
    ],
   }
const serializers = {
  types: {
   videoEmb: (props) => {
     // here i need more 
     // than just _ref 
     // of doc inside props.node.
     // I need referenced attrs
   }
  }
}
my mux flavored Videos schema type:
export default {
  title: "Videos",
  name: "videos",
  type: "document",
  fields: [
   ...
   {
      title: "Video file",
      name: "video",
      type: "mux.video",
      validation: Rule => Rule.required(),
    }
  ]
}
In fact i need infos in my rendere which typically are exposed by mux.video subtype.
Possible Solution in gatsby (where the renderer reides):I could do a graphQL query in Gatsby to fetch the document by ID in the renderer.

So is this the best approach to this?
Dont even know if dynamic params in graphql are possible in gatsby outside of a page component.
Hey Marc! Do you have a particular reason for using a reference to a Mux asset? If not, in order to avoid having to expand that reference, you could just add
{ type: 'mux.video'}
to your
blockContent
array. You could then serialize it on your frontend by passing the video asset to sanity-mux-player .
Thanks for the suggestion, but i always thought using the mux.video type gets me mainly the upload capability and not the “select from existing videos” capability. Will try it out now though. Just seen that there is a “Browse” button on that component which might does the trick for selecting existing ones.
(btw, i did manage to do it with GraphQL, Gatsby and the Reference type, but it might be a weird solution, so i will try your proposed one)
tried using mux.video but wont change the problem that i dont get the needed attributes. See the two different ways:

body:{} 2 items

1:{} 2 items

_type:videoEmb

video:{} 2 items

_ref:d970338c-1ebb-4f88-b9b4-cec9615c7e63

_type:reference

5:{} 3 items

_key:4f7911c13b96

_type:mux.video

asset:{} 1 item

_ref:29e883f1-21bb-44ff-ab79-d84a04169c0b
Both of them only get me a reference into the props of my serializer.
Ah, you're right! I was going based off of this guide , but it doesn't give any info for adapting this for Gatsby/GraphQL
I like using mux.video directly inside PortableText even more than my approach. Problem is, the ref i get back with mux.video is somewhat not usable. I cant find the data with GraphQL.
So i am stuck with my “reference” approach which is not the best solution since the preview is kind of bad in the RichText Editor
I'll look through our ticket archives and see if a solution has come up before.
thx
thx
Are you using gatsby-source-sanity?
yeah
thats how my current serializer for my Reference to VideoPost works. I use GraphQL for getting the VideoPost Page via Ref.
Ok, I think you can use raw fields to resolve the reference in your query as mentioned here . That should get the actual asset, not just the
_ref
.
looks promising…. thanks
Let me know how it goes!
will do….
Had the time to inspect the raw fields, in my case the raw body of the blogpost where the mux.video is embedded. I see this in graphql:
{
“_key”: “d4379ae7d5dd”,
“_type”: “mux.video”,
“asset”: {
“_ref”: “29e883f1-21bb-44ff-ab79-d84a04169c0b”
}
}

the _ref is exactly the ID i get in my serializer. But what to do with that? The expanded attribtues for this vid are in a different structure in graphql where no ID matches:

“video”: {
“asset”: {
“playbackId”: “a01Q5yI6RIQjV1MkobmASWbH8BKpxihKsSHp8ZBdyK5A”,
“thumbTime”: 0,
“_key”: null,
“assetId”: “2daMSf5936Y6fTuORL298HLtwfEjmStABjrriVoLR7Y”,
“_type”: “mux.videoAsset”,
“status”: “ready”
},
“_rawAsset”: {
“_ref”: “-d974861b-cac9-5f8b-a158-59418996435c”
}
},
“_id”: “d970338c-1ebb-4f88-b9b4-cec9615c7e63"

So i still dont have the chance to get the important attribtutes for my renderer when using mux.video directly in portableText. I cant be the only one trying this
🙂
ok. Got it._rawAsset(resolveReferences: {maxDepth: 2})

is the key.. meaning exactly specifying maxDepth: 2. This reveal all those important details the clip has. But who is maintaing the mux.video plugin? It really makes no sense to grab the duration out of the _raw fields. They should be in the same nice accesible structure in graphql as the others (like playbackId, thumbTime, assetId,….) I am not sure if this plugin is responsible for the structure in graphql, but if yes, this would be a really useful fix. Its not related to my main question btw.
can you pass this thumbTime issue to the right person? I really tried to fix it myself but need some advice… the github issues page for mux.video is kind of dead

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?