Hide already selected references from array dropdown in Sanity

8 replies
Last updated: Jul 5, 2022
Hi! Happy Friday!Is there something I can use , so the array of reference does not show me the Services that are already selected?
I would like to select only the one that are not on the list. For example if Orthopedics is already selected, it should be hidden on the Dropdown. Is it posible?
Thanks!
AI Update

Yes, absolutely! You can filter out already-selected references from the dropdown so editors only see options that haven't been added yet. This is a common need and there's a clean solution using the options.filter property on your reference field.

Here's how to set it up. The key is to place the filter on the reference field inside your array, not on the array itself:

{
  title: 'Services',
  name: 'services',
  type: 'array',
  of: [
    {
      type: 'reference',
      to: [{type: 'service'}],
      options: {
        filter: ({document}) => {
          // Get all currently selected service IDs
          const selectedServices = (document.services || [])
            .map(service => service._ref)
            .filter(Boolean)
          
          return {
            filter: '!(_id in $selectedServices)',
            params: {
              selectedServices
            }
          }
        }
      }
    }
  ]
}

This works by:

  1. Extracting the _ref values from all currently selected items in your array
  2. Using a GROQ filter to exclude documents whose _id matches any of those references
  3. The .filter(Boolean) removes any undefined values to prevent errors

Making it reusable

If you're using this pattern across multiple fields, you can create a utility function. Here's a reusable approach from the Sanity community:

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

Then in your schema:

import { getFilter } from '../utils/getFilter'

{
  title: 'Services',
  name: 'services',
  type: 'array',
  of: [
    {
      type: 'reference',
      to: [{type: 'service'}],
      options: {
        filter: ({document}) => getFilter(document, 'services')
      }
    }
  ]
}

This way, once Orthopedics is selected, it won't appear in the dropdown anymore when you go to add another service. The filter updates dynamically as you add or remove items from the array.

You can read more about dynamic reference filters in the Sanity docs and see additional discussion in this community thread about filtering reference arrays.

Happy Friday! šŸŽ‰

Show original thread
8 replies
you would probably have to create a custom input component to handle that logic, but i don't see why it wouldn't be possible
Mmmh, no I think you might be able to do it with just a filter.
Not sure. šŸ˜…
I’ve definitely done something like this before, and yes it made use of the filter property Kitty pointed to - let me see if I can dig it up
Aha, here we are - haven’t got the Studio for this particular project up and running atm so can’t confirm but something like this should be similar
export default {
  name: 'term',
  title: 'Term',
  type: 'document',
  fields: [
    {
      name: 'type',
      title: 'Name',
      type: 'string',
    },
    {
      name: 'childTerms',
      title: 'Child term(s)',
      type: 'array',
      of: [{
        title: 'Child term',
        type: 'reference',
        to: [{type: 'term'}],
        options: {
          filter: ({document}) => {
            // We need to provide both the draft and the live id (prefixed by 'drafts.' and without this prefix)
            // in order to exclude the current term from the list of possible child terms.
            const idWithoutDraft = document._id.replace('drafts.', '')
            return {
              filter: '!(_id in $selectedIds)',
              params: {
                selectedIds: [idWithoutDraft, document._id, ...document.childTerms.map(term => term._ref)]
              }
            }
          },
        },
      }]
    }
  ]
}

Hey
user S
, it was really helpful and it works fine when I use the standar schema type. My problem is that when I use the Sanity Schema Builder it comes with an error. Do you know if I can fix it?Works:

{
                name: 'childTerms',
                title: 'Child term(s)',
                type: 'array',
                of: [{
                  title: 'Child term',
                  type: 'reference',
                  to: [{type: 'pages'}],
                  options: {
                    filter: ({document}) => {
                        console.log(document);
                      // We need to provide both the draft and the live id (prefixed by 'drafts.' and without this prefix)
                      // in order to exclude the current term from the list of possible child terms.
                      const idWithoutDraft = document._id.replace('drafts.', '')
                      return {
                        filter: '!(_id in $selectedIds)',
                        params: {
                          selectedIds: [idWithoutDraft, document._id, ...document.childTerms.map(term => term._ref)]
                        }
                      }
                    },
                  },
                }]
            }  ,
Not Working:
S.array('testt', 'TEst').of([ 
                S.reference('pages', 'Select a Page')
                    .to([{type : 'pages'}])
                    .options({
                        filter: ({document}) => {
                            console.log(document);
                            // We need to provide both the draft and the live id (prefixed by 'drafts.' and without this prefix)
                            // in order to exclude the current term from the list of possible child terms.
                            const idWithoutDraft = document._id.replace('drafts.', '')
                            return {
                                filter: '!(_id in $selectedIds)',
                                params: {
                                    selectedIds: [idWithoutDraft, document._id, ...document.childTerms.map(term => term._ref)]
                                }
                            }
                        },
                    })
            ]),
Sorry, I’ve not used the Sanity Schema Builder before so I’m not sure about its usage/limitations

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?