Remove 'add item' button when array length validation is met

25 replies
Last updated: Apr 27, 2022
Hi Everyone!
I’m using an
array
with a
length validation
to limit the Array to 2 array items. Is there a way to remove the ‘add item’ button to disappear when the length validation is met?
AI Update

Unfortunately, Sanity Studio doesn't provide a built-in way to automatically hide the "Add Item" button when an array reaches its maximum validation length. The validation system will prevent publishing if the limit is exceeded, but the UI won't automatically adapt to disable or hide the add functionality.

However, you can achieve this with a custom input component that conditionally renders the array interface based on the current length. Here's the proper approach:

Solution: Custom Array Input Component

The key is to create a custom component that checks the array length against your validation rules and uses renderDefault with modified props to control the array's behavior:

// customArrayInput.js
import {ArrayOfObjectsInputProps} from 'sanity'
import {Stack, Text} from '@sanity/ui'

export function LimitedArrayInput(props: ArrayOfObjectsInputProps) {
  const {value = [], schemaType} = props
  
  // Extract max length from validation rules
  const maxRule = schemaType.validation
    ?.flatMap(v => v._rules || [])
    ?.find(rule => rule.flag === 'max')
  
  const maxLength = maxRule?.constraint
  const currentLength = value.length
  const isAtMax = maxLength && currentLength >= maxLength
  
  // When at max, render with readOnly to prevent adding more items
  if (isAtMax) {
    return (
      <Stack space={3}>
        {props.renderDefault({
          ...props,
          readOnly: true // This makes the entire array read-only
        })}
        <Text size={1} muted>
          Maximum of {maxLength} items reached. Remove an item to add more.
        </Text>
      </Stack>
    )
  }
  
  // Otherwise render normally
  return props.renderDefault(props)
}

Configure in Your Schema

Add the custom component to your array field definition using the components.input property:

import {defineField, defineArrayMember} from 'sanity'
import {LimitedArrayInput} from './customArrayInput'

export default defineField({
  name: 'myArray',
  type: 'array',
  title: 'My Limited Array',
  of: [
    defineArrayMember({type: 'string'})
  ],
  validation: Rule => Rule.max(2),
  components: {
    input: LimitedArrayInput
  }
})

Important Limitations

The approach above uses readOnly: true, which makes the entire array read-only when the limit is reached. This means:

  • ✅ Users cannot add new items (the add button becomes disabled)
  • ❌ Users also cannot edit or remove existing items while at the limit

If you need users to be able to remove items while at the max (so they can swap items), you'll need a more complex solution that fully reimplements the array input rather than using renderDefault.

Alternative: Conditional Field

Another approach is to use conditional fields to make the entire array field read-only based on its length:

defineField({
  name: 'myArray',
  type: 'array',
  of: [{type: 'string'}],
  validation: Rule => Rule.max(2),
  readOnly: ({value}) => (value || []).length >= 2
})

This has the same limitation as above – the entire field becomes read-only at the limit.

Why This Is Tricky

The reason there's no simple "hide add button" option is that Sanity's array input component is complex, handling drag-and-drop reordering, item editing, validation display, and more. The renderDefault function gives you the complete default component, but you can't easily modify just one aspect of it (like hiding a specific button) without reimplementing the entire component.

For most use cases, the read-only approach above strikes a good balance between customization effort and user experience – it clearly communicates that the limit has been reached while preventing further additions.

Show original thread
25 replies
user A
created this input component that will remove the add item button once your
max()
is hit.
Hi User where u able to solve this?
No, I moved past it. It looks like
user A
created a fix that I should try thought.
Glad you brought it up. Are you stuck on it?
user A
looks like that link isn’t available. Can you send another link to it?
I'm not stuck, I want to give my users a section where they can reorder the sections on their homepage, but I don't want to give them the option to add or remove any of the array items...
user S
I’m not sure it’s currently possible to allow re-ordering but not editing. I’ll look through some threads to confirm.
Thanks
user A
Yeah I'm going through this custom array component, but I didn't want to spend so much time on this... maybe I'll just set min and max in the array rules...
Maybe the solution is in
user S
Post order plugin

https://www.sanity.io/plugins/orderable-document-list
Oh yeah but I dont have documents, my document is the homepage, and inside the homepage I have an array of sections... but could this be done with documents instead?
In that case, would it work to set the object(s) in your array to read-only ? That—combined with the code in the gist above—seems like it would get you there.
yeah i tried read-only but then u cant reorder them. Ill see if i can adapt the custom array above.
Good point. Can you put
readOnly
on the fields themselves instead (i.e., each field inside the object)? That should let you re-order.
oh let me try that
ok so that doesn't let me move the object inside the array and i can still add items to the array...
Interesting. I tried setting up this schema:

export default {
  name: 'myArray',
  title: 'My Array',
  type: 'document',
  fields: [
    {
      name: 'title',
      title: 'Title',
      type: 'string',
    },
    {
      name: 'myArr',
      title: 'My Arr',
      type: 'array',
      validation: Rule => Rule.min(3).max(3),
      of: [
        {
          name: 'myObject',
          title: 'My Object',
          type: 'object',
          fields: [
            {
              name: 'title',
              title: 'Title',
              type: 'string',
              readOnly: true,
            }
          ]
        }
      ]
    }
  ]
}
Then implemented that gist, killed the studio process, and re-started it. I now get the behaviour in the enclosed video. The add button is gone, and although a user could duplicate with the three dots menu, they can’t publish. They can also remove an item to go below three, but also can’t publish. Since the fields are read-only, the add button that re-appears when there are &lt; 3 items isn’t much use, since they would need to edit a field in some way to “create” that item.
I see what you did here! this is awesome! thanks
user A
I had placed my read only on the object itself not on the items inseide
would the gist replace all the arrays? Or is there a way I can have both?
I had expected it to work the same whether
readOnly
was on the object or the fields of the object, so TIL as well.
like can I have these custom arrays
I see how it works! wow thanks
user A
!! 😄
The gist will go in a file somewhere in your studio folder, then you’ll add it as a part to sanity.json using the format in lines 7–10 of the gist. The file can be named whatever you want (it’s called customArrayFunctions.js in this case); just be sure to update the part path accordingly.
The code in the gist won’t interfere at all except when you set a
.max()
validation on an array. If you come across a case where you want to implement a max but not lose the add button, you’d want to rework the code in customArrayFunctions.js to more granularly and carefully apply itself.
No problem, and full credit to User . I just adapted it to work with Sanity UI.
Hey
user A
any idea how to remove the 3 dots on the right side? it opens up a menu to remove and add items.
I was playing around with the gist but can't seem to find where that menu comes from.

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?