Dynamic filters with reference to multiple types in Sanity

13 replies
Last updated: Dec 24, 2021
Hello!Not sure if it’s doable but, have any of you guys implemented
dynamic filters and reference multiple types at the same time?
I’m looking for something like the following code but having an array of references instead of a single reference:

{
  title: 'Director',
  name: 'director',
  type: 'reference',
  to: [{type: 'person'}],
  options: {
    filter: ({document}) => {
      // Always make sure to check for document properties
      // before attempting to use them
      if (!document.releaseYear) {
        return {
          filter: 'role == $role',
          params: {role: 'director'}
        }
      }
      
      return {
        filter: 'role == $role && birthYear >= $minYear',
        params: {
          role: 'director',
          minYear: document.releaseYear
        }
      }
    }
  }
}
AI Update

Yes, you can definitely implement dynamic filters and reference multiple types at the same time in Sanity! Your example code is on the right track, but you'll need to apply it to an array of references instead of a single reference field.

Array of References with Dynamic Filter

{
  title: 'Directors',
  name: 'directors',
  type: 'array',
  of: [
    {
      type: 'reference',
      to: [{type: 'person'}],
      options: {
        filter: ({document}) => {
          if (!document.releaseYear) {
            return {
              filter: 'role == $role',
              params: {role: 'director'}
            }
          }
          
          return {
            filter: 'role == $role && birthYear >= $minYear',
            params: {
              role: 'director',
              minYear: document.releaseYear
            }
          }
        }
      }
    }
  ]
}

Multiple Reference Types with Dynamic Filters

If you want to reference multiple document types with different filters for each:

{
  title: 'Crew Members',
  name: 'crewMembers',
  type: 'array',
  of: [
    {
      type: 'reference',
      to: [
        {type: 'person'},
        {type: 'company'}
      ],
      options: {
        filter: ({document}) => {
          if (!document.releaseYear) {
            return {
              filter: '_type in ["person", "company"]',
              params: {}
            }
          }
          
          return {
            filter: '(_type == "person" && birthYear >= $minYear) || _type == "company"',
            params: {
              minYear: document.releaseYear
            }
          }
        }
      }
    }
  ]
}

Key Points

  1. The options.filter goes inside each reference definition within the of array, not on the array itself
  2. Dynamic filters work the same way - they receive {document, parent, parentPath} and return {filter, params}
  3. Multiple types in to - you can reference multiple document types and filter across all of them using an array like [{type: 'person'}, {type: 'bovinae'}]
  4. Type-specific filtering - use _type == "typename" in your GROQ filter to apply different logic per type

The filter function has access to the parent document, so you can create conditional logic based on any field in your document, just like in your example with releaseYear.

According to the Sanity reference type documentation, the filter option can be either a string or a function. When you provide a function, it's called with an object containing document, parent, and parentPath properties, and it should return an object with filter and params properties. This works perfectly for arrays of references!

Show original thread
13 replies
Hi José! You can accomplish this with an array of references . I suspect you’ll want something like:

{
  name: "director",
  title: "Director",
  type: 'array',
  of: [
    {
      type: "reference",
      to: [{ type: "person" }, { type: "people" }, { type: "folks" }],
      options: {
        filter: ({ document }) => {
          // filter logic
        },
      },
    }
  ]
},
Thanks
user A
I tried that before but it seems like the
person | people | folk
data is not accesible at that point. I need to check for example, if
person | people | folk
have a
secondName
and if not they should not be listed in the dropdown.
My approach would be to remove the filter altogether and make sure the full data comes back, then build back in the filter incrementally. If you need any help, do follow up!
I’m sorry, probably the
secondName
field is not a good example. The filter that is need to filter by is actually an optional field, so I should assume that sometimes it will be null.
OR probably I’m not understanding your approach sorry if that’s the case.
What’s the
secondName
field?
Well, this is my actual code:
{
      name: "cards",
      title: "Assets cards",
      type: "array",
      of: [
        {
          type: "reference",
          to: [{ type: "asset" }, { type: "page" }, { type: "post" }],
        },
      ],
},
And what I need to do is to filter assets, pages and posts with a
featuredImage
present.
We want to prevent users from selecting assets, pages or posts without a
featuredImage
which is an optional field.
How about this? It will only return documents where the
featuredImage.asset
object is not null, which indicates there’s a reference to an image asset present.

{
  name: "cards",
  title: "Assets cards",
  type: 'array',
  of: [
    {
      type: "reference",
      to: [{ type: "asset" }, { type: "page" }, { type: "post" }],
      options: {
        filter: 'featuredImage.asset != null',
      },
    }
  ]
},
😮 yeah I will try that, thanks a lot
user A
This may be a case where you want to disable inline reference creation , since from a quick test, I believe your users would be able to create a reference without a
featuredImage
and the picker would allow it.
That’s right, I’ll also add
disableNew: true

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?