`slug` is `undefined` when I track mark and children from internalLink

4 replies
Last updated: Dec 14, 2020
I’m getting this from my console when I track
mark
and
children
from
internalLink
:
internalLink - mark 
_key: "7e6ceaaccca5"
_type: "internalLink"
reference: {…}
    _ref: "6230af03-9c14-4316-aa6f-ba7903d72c51"
    _type: "reference"

internalLink - children 
_key: "7e6ceaaccca5"
_type: "internalLink"
reference: {…}
    _ref: "6230af03-9c14-4316-aa6f-ba7903d72c51"
    _type: "reference"

slug
is
undefined
... arrrgh!What ... am I doing wrong?!
AI Update

The issue you're experiencing is that you're looking at the annotation object itself (which only contains the reference ID), but you need to resolve the reference to get the actual document data including the slug.

When you see reference: { _ref: "6230af03-9c14-4316-aa6f-ba7903d72c51" }, that's just a pointer to another document. The slug lives in the referenced document, not in the annotation object.

The Solution: Use GROQ to Expand References

You need to use GROQ's reference expansion operator (->) to fetch the actual document data. Here's how:

*[_type == "yourDocType"] {
  content[] {
    ...,
    markDefs[] {
      ...,
      _type == "internalLink" => {
        ...,
        "slug": reference->slug.current,
        "title": reference->title
      }
    }
  }
}

This tells GROQ to follow the reference and grab the slug from the referenced document.

If You're Rendering on the Frontend

If you're working with already-fetched data and need to resolve references client-side, you have a few options:

Option 1: Fetch with proper projection (recommended) Make sure your initial GROQ query expands the reference as shown above.

Option 2: Custom serializer with additional fetch If you're using @portabletext/react or similar, you can create a custom mark component that fetches the referenced document:

const components = {
  marks: {
    internalLink: ({value, children}) => {
      // value.reference._ref exists, but you need to fetch the document
      // OR better yet, ensure your GROQ query already expanded it
      const href = value.slug || '#'
      return <a href={href}>{children}</a>
    }
  }
}

Why This Happens

Sanity uses references to avoid data duplication and maintain referential integrity. The annotations documentation shows that internal link annotations store a reference object, not the full document data. You must explicitly tell GROQ to expand that reference using the -> operator.

The key takeaway: references are pointers, not the actual data. Always use -> in your GROQ queries to dereference them!

mmm... maybe I have to use
_rawText(resolveReferences: {maxDepth: 10})
in my page graphql query instead of just
_rawText
to get the slug
OK I think I begin to understand how this works!So I used the
resolveReferences
as written above and I did it like this :
  marks: {
    strong: (props) => <strong>{props.children}</strong>,
    em: (props) => <em>{props.children}</em>,
    internalLink: ({ mark: { reference }, children }) => {
      const { slug = {} } = reference;
      const href = `/${slug.current}`;
      return <Link to={href}>{children}</Link>;
    },
  },
and it works like a charm :)
Way to go David! I really like how you solved this thread by yourself 😄
Thanks David! After trying to work through this guide: https://www.sanity.io/guides/portable-text-internal-and-external-links I got stuck in the same spot.

This made the difference:
Guide Shows:

internalLink: ({mark, children}) => {
Changed it to:

internalLink: ({ mark: { reference }, children })

You saved the day!!!!
🏆

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?