
Grab your gear: The official Sanity swag store
Read Grab your gear: The official Sanity swag storeGreat question! Yes, you can definitely create a better UX for adding multiple tags at once. You'll need to build a custom input component that accepts comma-separated input and converts it to tag references.
Here's a working approach:
import {set, unset, insert, setIfMissing} from 'sanity'
import {Stack, TextInput, Button, Card, Text, Flex} from '@sanity/ui'
import {useCallback, useState} from 'react'
import {useClient} from 'sanity'
function TagsInput(props) {
const {value = [], onChange, schemaType} = props
const [inputValue, setInputValue] = useState('')
const client = useClient({apiVersion: '2023-01-01'})
const handleAddTags = useCallback(async () => {
// Split by comma and clean up
const tagNames = inputValue
.split(',')
.map(t => t.trim())
.filter(Boolean)
if (tagNames.length === 0) return
// Create or fetch tag documents
const tagRefs = await Promise.all(
tagNames.map(async (tagName) => {
const slug = tagName.toLowerCase().replace(/\s+/g, '-')
// Check if tag already exists
const existingTag = await client.fetch(
`*[_type == "tag" && slug.current == $slug][0]`,
{slug}
)
if (existingTag) {
return {
_type: 'reference',
_ref: existingTag._id,
_key: `tag-${Date.now()}-${Math.random()}`
}
}
// Create new tag document
const newTag = await client.create({
_type: 'tag',
title: tagName,
slug: {_type: 'slug', current: slug}
})
return {
_type: 'reference',
_ref: newTag._id,
_key: `tag-${Date.now()}-${Math.random()}`
}
})
)
// Merge with existing tags
const newValue = [...(value || []), ...tagRefs]
onChange(set(newValue))
setInputValue('')
}, [inputValue, value, onChange, client])
return (
<Stack space={3}>
<Flex gap={2}>
<Card flex={1}>
<TextInput
value={inputValue}
onChange={(e) => setInputValue(e.currentTarget.value)}
placeholder="Enter tags separated by commas"
onKeyDown={(e) => {
if (e.key === 'Enter') {
e.preventDefault()
handleAddTags()
}
}}
/>
</Card>
<Button onClick={handleAddTags} text="Add Tags" tone="primary" />
</Flex>
{value && value.length > 0 && (
<Text size={1} muted>
{value.length} tag{value.length !== 1 ? 's' : ''} selected
</Text>
)}
{/* Render the default array input below */}
{props.renderDefault(props)}
</Stack>
)
}
// In your schema:
export default {
name: 'post',
type: 'document',
fields: [
{
name: 'tags',
title: 'Tags',
type: 'array',
of: [{type: 'reference', to: {type: 'tag'}}],
components: {
input: TagsInput
}
}
]
}How this works:
renderDefault(props) call at the end shows the standard array input below your custom input, so you can still manage tags individually if neededKey points:
useClient() to access Sanity's client for creating/fetching tag documents_key values for each reference (required for array items)renderDefault() line if you want only the comma-separated inputThis gives you the best of both worlds - quick bulk tag entry while keeping your clean reference-based data structure intact!
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.
Content operations
Content backend


The only platform powering content operations
By Industry


Tecovas strengthens their customer connections
Build and Share

Grab your gear: The official Sanity swag store
Read Grab your gear: The official Sanity swag store