GROQ query not adding fields to markDefs for internal links

11 replies
Last updated: Aug 12, 2020
Hello! I'm trying to implement the internal link as explained here: https://www.sanity.io/guides/portable-text-internal-and-external-links
When I want to add the slug using a the GROQ query given, it does not seem to work. Nothing is added to the markDefs object when I test it in Vision:

*[_type == "post"]{
  ...,
  body[]{
    ...,
    markDefs[]{
      ...,
      _type == "internalLink" => {
        "slug": @.reference->slug
      }
    }
  }
}
In fact, even if I type something directly in the query, nothing is added to the markDefs:


*[_type == "page"]{
  ...,
  body[]{
    ...,
    markDefs[]{
      ...,
     _type == "internalLink" => {
        "something": "test"
      }
  }
}
Even if I remove the _type == "internalLink", it does not get written into the result.

Can somebody please help me and tell me what I am doing wrong?
AI Update

I can see the issue you're running into! The problem is actually simpler than you might think. Your GROQ query syntax is correct, but the conditional projection with => isn't adding fields to your result because there are no markDefs objects matching your condition or the reference isn't resolving properly.

Let's debug this step by step:

Why your query isn't working

The conditional projection syntax _type == "internalLink" => { "slug": @.reference->slug } is correct according to the official guide you're following. The => operator expands the result into the parent projection when the condition is true.

However, there are several common reasons why you're seeing no results:

1. No matching annotations exist

The most common issue: you might not have any markDefs with _type == "internalLink" in your content.

Test this first:

*[_type == "post"][0].body[].markDefs[]

This will show you the raw structure of your markDefs. Look for objects where _type == "internalLink" exists. If you don't see any, you need to add some internal links in your editor first.

2. The reference field name is different

Your schema might use a different field name than reference. Check your annotation schema definition - it could be item, document, ref, or something else.

3. Slug is an object, not a string

In Sanity, slug fields are typically objects like {current: "my-slug"}, not strings. Try:

*[_type == "post"]{
  ...,
  body[]{
    ...,
    markDefs[]{
      ...,
      _type == "internalLink" => {
        "slug": @.reference->slug.current
      }
    }
  }
}

4. The reference is broken

If the referenced document was deleted, @.reference-> returns null. Test with:

*[_type == "post"]{
  body[]{
    markDefs[]{
      ...,
      _type == "internalLink" => {
        "refExists": defined(@.reference->_id),
        "slug": @.reference->slug.current
      }
    }
  }
}

About your "something": "test" example

When you tried this:

markDefs[]{
  ...,
  _type == "internalLink" => {
    "something": "test"
  }
}

If this didn't add anything, it means no markDefs objects have _type == "internalLink". The conditional only executes when the condition is true. If there are no matching objects, nothing gets added.

The correct mental model

The => operator works like this: "When this condition is true, expand this object into the parent projection." It doesn't create a new object scope that needs spreading - it merges the result directly into the parent.

So your original query structure is correct:

markDefs[]{
  ...,                              // All existing fields
  _type == "internalLink" => {      // IF this is true, THEN...
    "slug": @.reference->slug.current  // ...add this field
  }
}

Start with the debugging queries above to see what's actually in your markDefs, and you'll quickly find where the disconnect is. Most likely, you either don't have any internal link annotations yet, or the field names don't match what you expect.

Show original thread
11 replies
Could an example your data? That would be helpful.
Just from the look if it, I think don't think the conditional should be in the
markDefs
array, but rather one level up, in
body
like this:

*[_type == "post"]{
  ...,
  body[]{
    ...,
    _type == "internalLink" => {
      "slug": @.reference->slug
    }
  }
}
Does that help?
Could you post an example of some body data? Would be helpful to see the structure of it.
{
  "_createdAt": "2020-04-02T12:35:58Z",
  "_id": "0ad5ed76-14b6-43fb-bc3f-f077c6f22279",
  "_rev": "5wdIb9L6kFflQqbpITq6F3",
  "_type": "page",
  "content": [
    {
      "_key": "cb02625fd1ee",
      "_type": "textSection",
      "body": [
        {
          "_key": "d1c8b8608870",
          "_type": "block",
          "children": [
            {
              "_key": "d1c8b86088700",
              "_type": "span",
              "marks": [],
              "text": "Here is a heading"
            }
          ],
          "markDefs": [],
          "style": "h1"
        },
        {
          "_key": "0f63f6e1823d",
          "_type": "block",
          "children": [
            {
              "_key": "0f63f6e1823d0",
              "_type": "span",
              "marks": [],
              "text": "Here is some text."
            }
          ],
          "markDefs": [],
          "style": "normal"
        },
        {
          "_key": "3c2e5e258014",
          "_type": "block",
          "children": [
            {
              "_key": "3c2e5e2580140",
              "_type": "span",
              "marks": [],
              "text": "Here I put a "
            },
            {
              "_key": "3c2e5e2580141",
              "_type": "span",
              "marks": [
                "f4e218586de7"
              ],
              "text": "link"
            },
            {
              "_key": "3c2e5e2580142",
              "_type": "span",
              "marks": [],
              "text": " somewhere."
            }
          ],
          "markDefs": [
            {
              "_key": "f4e218586de7",
              "_type": "link",
              "href": "<https://somesite.com>"
            }
          ],
          "style": "normal"
        },
        {
          "_key": "6c2615f2c05a",
          "_type": "block",
          "children": [
            {
              "_key": "6c2615f2c05a0",
              "_type": "span",
              "marks": [],
              "text": "This is "
            },
            {
              "_key": "6c2615f2c05a1",
              "_type": "span",
              "marks": [
                "u"
              ],
              "text": "underlined"
            },
            {
              "_key": "6c2615f2c05a2",
              "_type": "span",
              "marks": [],
              "text": "."
            }
          ],
          "markDefs": [],
          "style": "normal"
        },
        {
          "_key": "864868246e17",
          "_type": "block",
          "children": [
            {
              "_key": "864868246e170",
              "_type": "span",
              "marks": [
                "em"
              ],
              "text": "Cursive text"
            },
            {
              "_key": "864868246e171",
              "_type": "span",
              "marks": [],
              "text": "."
            }
          ],
          "markDefs": [],
          "style": "normal"
        },
        {
          "_key": "8db586a956b6",
          "_type": "block",
          "children": [
            {
              "_key": "8db586a956b60",
              "_type": "span",
              "marks": [],
              "text": ""
            },
            {
              "_key": "8db586a956b61",
              "_type": "span",
              "marks": [
                "d8aaeb931f05"
              ],
              "text": "This is an internal link"
            },
            {
              "_key": "8db586a956b62",
              "_type": "span",
              "marks": [],
              "text": "."
            }
          ],
          "markDefs": [
            {
              "_key": "d8aaeb931f05",
              "_ref": "211d539f-7e8c-4f0f-b87c-f2fcbda6228a",
              "_type": "internalLink"
            }
          ],
          "style": "normal"
        },
        {
          "_key": "aff49ec10194",
          "_type": "block",
          "children": [
            {
              "_key": "aff49ec101940",
              "_type": "span",
              "marks": [],
              "text": ""
            }
          ],
          "markDefs": [],
          "style": "normal"
        }
      ]
    },
    {
      "_key": "ede6681c4907",
      "_type": "twoColumn",
      "bodyLeft": [
        {
          "_key": "a34bf5b12787",
          "_type": "block",
          "children": [
            {
              "_key": "a34bf5b127870",
              "_type": "span",
              "marks": [],
              "text": "This is the left column contents."
            }
          ],
          "markDefs": [],
          "style": "normal"
        }
      ],
      "bodyRight": [
        {
          "_key": "6f94ca0e7826",
          "_type": "block",
          "children": [
            {
              "_key": "6f94ca0e78260",
              "_type": "span",
              "marks": [],
              "text": "Thsi is right column text."
            }
          ],
          "markDefs": [],
          "style": "normal"
        },
        {
          "_key": "88dd6d8ca0c5",
          "_type": "block",
          "children": [
            {
              "_key": "88dd6d8ca0c50",
              "_type": "span",
              "marks": [],
              "text": "Here comes a list:"
            }
          ],
          "markDefs": [],
          "style": "normal"
        },
        {
          "_key": "a1e220a3c068",
          "_type": "block",
          "children": [
            {
              "_key": "a1e220a3c0680",
              "_type": "span",
              "marks": [],
              "text": "Number 1"
            }
          ],
          "level": 1,
          "listItem": "bullet",
          "markDefs": [],
          "style": "normal"
        },
        {
          "_key": "c66d6ca5dc4d",
          "_type": "block",
          "children": [
            {
              "_key": "c66d6ca5dc4d0",
              "_type": "span",
              "marks": [],
              "text": "Number 2"
            }
          ],
          "level": 1,
          "listItem": "bullet",
          "markDefs": [],
          "style": "normal"
        },
        {
          "_key": "8c9bd044ab22",
          "_type": "block",
          "children": [
            {
              "_key": "8c9bd044ab220",
              "_type": "span",
              "marks": [],
              "text": "Dont forget 3"
            }
          ],
          "level": 1,
          "listItem": "bullet",
          "markDefs": [],
          "style": "normal"
        }
      ],
      "heading": "Here comes two columns"
    }
  ],
  "openGraphImage": {
    "_type": "image",
    "asset": {
      "_ref": "image-e4cf4bc559e435f458a659bc5abdd83fea6c6304-800x606-jpg",
      "_type": "reference"
    }
  },
  "slug": {
    "_type": "slug",
    "current": "test"
  },
  "title": "This is the heading",
  "_updatedAt": "2020-06-05T06:14:27.247Z"
}
Yes, here is a copy from "Inspect" of the page:
Actually I now changed a bit how the portableText object is constructed. I made the internalLink its own object:

export default {
    title: 'Portable Text',
    name: 'portableText',
    type: 'array',
    of: [
      {
        type: 'block',
        styles: [
          { title: 'Normal', value: 'normal' },
          { title: 'H1', value: 'h1' },
          { title: 'H2', value: 'h2' },
          { title: 'H3', value: 'h3' },
          { title: 'H4', value: 'h4' },
          { title: 'Quote', value: 'blockquote' },
        ],
        // Marks let you mark up inline text in the block editor.
        marks: {
          // Decorators usually describe a single property – e.g. a typographic
        // preference or highlighting by editors.
          decorators: [
            { title: 'Strong', value: 'strong' },
            { title: 'Emphasis', value: 'em' },
            { title: 'Code', value: 'code' },
            {
              title: 'Underline',
              value: 'u',
              blockEditor: {
                render: LinkRender
              }
            }
          ],
          annotations: [
          { type: 'link' }, 
          { type: 'internalLink' },
        ],
        },
      },
      { type: 'image' }
    ],
  };
And


export default {
  title: 'Internal link to another document',
  name: 'internalLink',
  type: 'reference',
  description: 'Locate a document you want to link to',
  to: [{ type: 'userPage'}, { type: 'page' }],
  blockEditor: {
    icon: () => ':link:',
    render: InternalLinkRender,
  },
};
But still I did not get the GROQ query to write anything in the result. I also tried ""slug": @.reference-&gt;slug.*current*"
I tried to replicate your data here, and it seems to work https://groq.dev/33ls10IOb2um1bqXCrrq0t
I just realised that you were missing the
content[]
array in your first query
Yes, I noticed that now too, and also there was the @.*reference-*&gt;slug, but in your example, which works, it was @ -&gt; .slug.current without the reference word. Thank you for your help!
Glad it worked!
Thanks
user T
it seems to work for me as well… I’m wondering why it the documentation shows @.reference if that does not work?

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?