Filter array to exclude already referenced sections in Sanity document

26 replies
Last updated: Jan 28, 2026
Hello amazing Sanity people 🌞 ! I am doing my head in trying to figure out how to write a filter query.I have a document with an array called
primary

primary
is an array of
item
objects.In item, instead of getting a list of all sections, I only want to see those which are not already referenced by my document.
Maybe this is actually very simple, but I can’t seem to work it out…

  name: 'item',
  title: 'Item',
  type: 'object',
  fields: [
    {
      name: 'section',
      title: 'Section',
      type: 'reference',
      to: { type: 'section' },
      options: {
        filter: ({ document, parent, parentPath }) => {
          console.log(document._id)

          return {
            filter: '!(^._id in *[_id==$document].primary[])',
            params: {
              document: document._id
            }
          }
        }
      }
    }
  ]

AI Update

I can help you filter out already-referenced sections from your reference field! The issue with your current query is the syntax - you're trying to access the parent document's array, but the approach needs adjustment.

Here's the corrected solution:

{
  name: 'section',
  title: 'Section',
  type: 'reference',
  to: { type: 'section' },
  options: {
    filter: ({ document }) => {
      // Get all section IDs already referenced in the primary array
      const referencedIds = document.primary
        ?.map(item => item.section?._ref)
        .filter(Boolean) || []

      return {
        filter: '!(_id in $referencedIds)',
        params: {
          referencedIds
        }
      }
    }
  }
}

What's happening here:

  1. Extract referenced IDs in JavaScript: Instead of trying to do this purely in GROQ, we extract the _ref values from the primary array items in the filter function itself. This is more reliable since you have direct access to the document object.

  2. Simple GROQ filter: The GROQ filter !(_id in $referencedIds) checks if the current section's _id is NOT in the array of already-referenced IDs.

  3. Pass as params: We pass the array of IDs as a parameter to the GROQ query.

Important note: The filter function re-runs when the document changes, so as you add sections to your primary array, they'll automatically disappear from the available options in other items.

Making it reusable:

You can extract this into a utility function to keep your code DRY, as shown in this community example:

// utils/getFilter.js
export const getFilter = (document, field, refField = '_ref') => {
  const existingEntries = document[field]
    ?.map(item => item[refField])
    .filter(Boolean) || []
  
  return {
    filter: '!(_id in $existingEntries)',
    params: {
      existingEntries
    }
  }
}

// Then in your schema:
options: {
  filter: ({ document }) => {
    const referencedIds = document.primary
      ?.map(item => item.section?._ref)
      .filter(Boolean) || []
    
    return {
      filter: '!(_id in $referencedIds)',
      params: { referencedIds }
    }
  }
}

This is a common pattern when building navigation menus, related content pickers, or any scenario where you want to avoid duplicate references in an array!

Show original thread
26 replies

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?