Populate string list based on another field value in same document
I'm wondering if there is a way to populate a list of predefined strings based on the value of another field in the same document?
Basically what I'm hoping the achieve is this. I have a
collectiondocument that references
productsand the
collectioncan conditionally have sub-categories which are just added as string tags. I was hoping to be able to add a
productand be able to give it a sub-category based on the ones already defined in the
collection. I've been able to get it so the editor can just type in a category and it will be invalid unless it matches one already defined in the sub-categories field but this seems a bit hacky. Any advice on how best to achieve this? Perhaps custom input component?
Yes, you can definitely populate a list of predefined strings based on another field's value! This is a common pattern and there are a couple of good approaches depending on your needs.
Custom Input Component (Recommended Approach)
The most flexible solution for your use case is creating a custom input component. This gives you full access to the Sanity client and the useFormValue hook to read values from other fields in the document:
import { useFormValue, useClient, StringInputProps, set } from 'sanity'
import { useEffect, useState } from 'react'
function SubCategoryInput(props: StringInputProps) {
const client = useClient({ apiVersion: '2023-01-01' })
const [options, setOptions] = useState<string[]>([])
// Get the collection reference from the current document
const collectionRef = useFormValue(['collection', '_ref'])
useEffect(() => {
if (!collectionRef) return
// Fetch the collection document and extract sub-categories
client.fetch(
`*[_id == $ref][0].subCategories`,
{ ref: collectionRef }
).then((subCategories) => {
setOptions(subCategories || [])
})
}, [collectionRef, client])
return (
<select
value={props.value || ''}
onChange={(e) => props.onChange(set(e.target.value))}
>
<option value="">Select a sub-category</option>
{options.map(category => (
<option key={category} value={category}>{category}</option>
))}
</select>
)
}
// In your product schema:
{
name: 'subCategory',
type: 'string',
title: 'Sub Category',
components: {
input: SubCategoryInput
}
}You can also check out this community pattern for building async list options.
Alternative: Using References
Consider whether your sub-categories should actually be separate documents that you reference, rather than strings. This approach gives you:
- Better data normalization
- Easier querying and reuse
- Built-in reference selection UI with filtering
// Product schema with filtered references
{
name: 'subCategory',
type: 'reference',
to: [{ type: 'subCategory' }],
options: {
filter: ({ document }) => {
const collectionRef = document.collection?._ref
return {
filter: '_id in *[_id == $collectionRef][0].subCategories[]._ref',
params: { collectionRef }
}
}
}
}Why Not options.list as a Function?
While it would be nice to use options.list as an async function (as discussed in this GitHub issue), it's currently not supported because:
options.listfunctions are synchronous and can't use async operations- You can't directly access the Sanity client within the schema definition
The custom input component approach is the current best practice for dynamic lists that depend on other field values or require data fetching. It gives you full control and access to all the hooks you need.
Show original thread2 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.