Limit array items to single instance using custom validation in Sanity

4 replies
Last updated: Jul 29, 2020
i have an array of different content modules that make up the pages of a website. most of the modules can be repeated freely throughout a page, but there are a few modules that i'd like to limit to 1 per array. assuming this would be done using
custom
validation? has anyone done this?
AI Update

Yes, custom validation is exactly the right approach for this! You can use the custom() validation method to count how many times a specific module type appears in your array and limit it to 1.

Here's a practical example of how to implement this:

defineField({
  name: 'pageModules',
  type: 'array',
  of: [
    { type: 'heroModule' },
    { type: 'textModule' },
    { type: 'ctaModule' }, // Let's say this one should only appear once
    { type: 'galleryModule' },
  ],
  validation: (rule) =>
    rule.custom((modules) => {
      if (!modules) return true;
      
      // Count occurrences of the module type you want to limit
      const ctaCount = modules.filter((module) => module._type === 'ctaModule').length;
      
      if (ctaCount > 1) {
        return 'Only one CTA module is allowed per page';
      }
      
      return true;
    }),
})

If you have multiple module types that need this restriction, you can make it more scalable:

validation: (rule) =>
  rule.custom((modules) => {
    if (!modules) return true;
    
    // Define which module types should be limited to 1
    const singleUseModules = ['ctaModule', 'heroModule', 'footerModule'];
    
    for (const moduleType of singleUseModules) {
      const count = modules.filter((module) => module._type === moduleType).length;
      
      if (count > 1) {
        return `Only one ${moduleType} is allowed per page`;
      }
    }
    
    return true;
  }),

You can also add this validation at the individual module level within the array, which will show the error more specifically on the offending item:

{
  type: 'ctaModule',
  validation: (rule) =>
    rule.custom((value, context) => {
      const modules = context.parent as any[];
      const ctaCount = modules?.filter((m) => m._type === 'ctaModule').length || 0;
      
      if (ctaCount > 1) {
        return 'Only one CTA module allowed per page';
      }
      
      return true;
    }),
}

The validation system runs client-side in the Studio and will show an error message when editors try to add more than one of the restricted module types, preventing them from publishing until they fix it. You can read more about custom validation rules in the Sanity documentation.

Show original thread
4 replies
not very sure about this but I think
__experimental_actions: [/*'create',*/ 'update', /*'delete',*/ 'publish']
can be a solution. This limits the document or object to be updated and published but not created or deleted. I believe it removes the create and delete buttons
this is usually used with siteSettings which is usually used once through out the site
this isn't quite what im looking for
i think i solved the problem thank you!

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?