Joint session with Vercel: How to build intelligent storefronts (May 15th)

Change `tags` layout to be an autocomplete kind of deal.

4 repliesLast updated: Nov 29, 2025

Hey folks! 👋 I feel like I should be able to figure this out on my own, but I'm stuck and would be grateful for any help 🙏
I have a field to add tags to photos, nothing fancy:


{
      name: 'tags',
      title: 'Tags',
      type: 'array',
      description: 'Add tags that describe this photo.',
      of: [{type: 'string'}],
      options: {
        layout: 'tags',
        isHighlighted: true
      },
      validation: Rule => Rule.required().min(2).error('At least 2 tags are required.')
}
What I'm trying to do is to change
tags
layout to be an autocomplete kind of deal. When a person starts to type a tag, I want to check all existing tags that start with this input (this is the easy part) and display them as an autocomplete dropdown for this
tags
field (this is the hard part). On Enter, the tag selected from the dropdown is added to the array, and the user can start typing next tag with the same autocomplete dropdown experience.
Any ideas or pointers? Thank you so very much!
🙇‍♂️
AI Update

Great question! You'll want to create a custom input component that replaces the default array string input with an autocomplete experience. Here's how to approach this:

The Solution

You need to build a custom input component that uses the Sanity UI Autocomplete component and queries existing tags from your content. Here's the basic structure:

import {set, unset} from 'sanity'
import {Autocomplete, Card} from '@sanity/ui'
import {useCallback, useState, useEffect} from 'react'
import {useClient} from 'sanity'

function TagsAutocompleteInput(props) {
  const {onChange, value = [], schemaType} = props
  const [inputValue, setInputValue] = useState('')
  const [existingTags, setExistingTags] = useState([])
  
  const client = useClient({apiVersion: '2024-01-01'})
  
  // Fetch existing tags from your content
  useEffect(() => {
    client.fetch(`array::unique(*[defined(tags)].tags[])`)
      .then(setExistingTags)
  }, [client])
  
  // Filter tags based on input
  const suggestions = existingTags.filter(tag => 
    tag.toLowerCase().startsWith(inputValue.toLowerCase())
  )

  const handleSelect = useCallback((selectedTag) => {
    // Add the selected tag to the array
    const newValue = [...(value || []), selectedTag]
    onChange(set(newValue))
    setInputValue('') // Clear input for next tag
  }, [value, onChange])

  const handleRemoveTag = useCallback((index) => {
    const newValue = value.filter((_, i) => i !== index)
    onChange(newValue.length ? set(newValue) : unset())
  }, [value, onChange])

  return (
    <Card>
      {/* Display existing tags with remove buttons */}
      <div>
        {value?.map((tag, index) => (
          <span key={index}>
            {tag}
            <button onClick={() => handleRemoveTag(index)}>×</button>
          </span>
        ))}
      </div>
      
      {/* Autocomplete input */}
      <Autocomplete
        options={suggestions.map(tag => ({value: tag}))}
        value={inputValue}
        onChange={setInputValue}
        onSelect={handleSelect}
        placeholder="Type to search tags..."
      />
    </Card>
  )
}

Integrate it into your schema

Modify your field definition to use the custom component:

{
  name: 'tags',
  title: 'Tags',
  type: 'array',
  of: [{type: 'string'}],
  components: {
    input: TagsAutocompleteInput
  },
  validation: Rule => Rule.required().min(2).error('At least 2 tags are required.')
}

Fetching Existing Tags

The GROQ query array::unique(*[defined(tags)].tags[]) flattens all tag arrays across your documents and returns unique values - perfect for your autocomplete suggestions!

Key Points

Check out the custom input component guide for more detailed examples and best practices. The guide walks through all the props available and how to properly handle patch events!

Show original thread
4 replies

Was this answer helpful?

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.

Related contributions