How to set slug source to object's own title field in Sanity array?

8 replies
Last updated: Apr 29, 2021
I have a "page" document which contains both a "title" string and an array of objects, one of which I'm calling "headingBlock". The object also has a "title" string and a slug, and I want to use the "title" string of the object as the slug source. But I can only get it to use the document title string as source, not the title string of the "headingBlock" object that the slug is a part of (sorry for the confusing description). Any idea what to set the slug source to?
AI Update

When you have a slug field nested inside an object (like your headingBlock within an array), the source option needs to reference the field relative to that object's context, not the document level.

The key is using the parent parameter in the source function. Instead of:

source: 'title'

You need:

source: (doc, { parent }) => parent.title

Here's how this works in your case:

{
  name: 'headingBlock',
  type: 'object',
  fields: [
    {
      name: 'title',
      type: 'string',
      title: 'Title'
    },
    {
      name: 'slug',
      type: 'slug',
      options: {
        source: (doc, { parent }) => parent.title,
        maxLength: 96
      }
    }
  ]
}

The issue you're experiencing is that when a slug field is inside a nested object or array item, it's looking for the source field at the document root level by default. By using the function form of the source option, you get access to both doc (the entire document) and parent (the immediate parent object containing this slug field).

So parent.title refers to the title field within the same headingBlock object, rather than trying to find a title at the document root level (which would be the page document's title string).

This is a common gotcha when working with nested structures in Sanity schemas, and the parent accessor is specifically designed to solve this problem! I found a similar case in the Sanity community answers where someone had the exact same issue with an event object nested in an array, and using parent.eventName solved it.

Show original thread
8 replies
I see... I tested now and confirm the behavior you're saying. If I have two fields in the parent document called 'heading' and 'heading2', then the slug generation button in the 'heading2' object would get the 'title' string from the 'heading' object.
Is there no way around this behavior? If not, isn't this really a bug? When creating an object, the functionality of that object shouldn't be concerned about what name it is given when used. And in my case it's used in an array, so it cannot know what index number it has or if there are other objects of the same type in the array.
I definitely see your point there. An object should be able to operate without any awareness of what it’s contained within. I wonder if there’s a way you can dynamically set it so that you can sort of avoid this?
That said, I think these are valid points and you should share them in the
ideas-and-feedback channel or open up an issue on Github .
options.source
can be a function. Does this achieve what you’re after?

{
  name: 'slug',
  title: 'Slug',
  type: 'slug',
  options: {
    source: (_, { parent: { title } }) => title,
    maxLength: 96,
  },
},
Ah, so awesome! Worked like a charm! Can you elaborate a little on what that statement actually does?
Great! Yes, sorry I didn’t to start with. As a function, slug can take two parameters :
doc
and
options
. We don’t need
doc
in this case so convention is to use an underscore to represent it.
{ parent: { title } }
is destructuring `options`; the function could just as well be written:

source: (doc, options) => {
  const { parent } = options
  const { title } = parent
  return title
}
or


source: function(doc, options) {
  return options.parent.title
}

user A
thank you so much!
No problem!
Thank you for the help! 🙂

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?