Convert comma-separated string to multiple tag references in Sanity array
12 replies
Last updated: Dec 29, 2022
AI Update
Great 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:
- The custom input provides a text field where you can enter comma-separated tag names
- When you click "Add Tags" (or press Enter), it splits the string and processes each tag
- For each tag name, it checks if a tag document with that slug already exists
- If it exists, it creates a reference to it; if not, it creates a new tag document first
- All the new references are added to your existing array using the set() patch operation
- The
renderDefault(props)call at the end shows the standard array input below your custom input, so you can still manage tags individually if needed
Key points:
- Uses
useClient()to access Sanity's client for creating/fetching tag documents - Generates unique
_keyvalues for each reference (required for array items) - Automatically creates slugs from tag titles
- Maintains your existing reference-based structure
- You can remove the
renderDefault()line if you want only the comma-separated input
This gives you the best of both worlds - quick bulk tag entry while keeping your clean reference-based data structure intact!
Show original thread12 replies
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.