Is it possible to dynamically populate the values in a List for a string type?
Yes, it is definitely possible to dynamically populate the values in a list for a string type! While Sanity doesn't natively support passing a function directly to options.list in your schema definition, there's a well-established workaround using a custom input component.
The approach involves creating a custom component that uses React hooks to fetch or compute your list options dynamically, then passes them to the default string input using renderDefault. Here's how it works:
Basic Example
In your schema, define a string field with an empty list and add any custom options you need:
{
name: 'region',
type: 'string',
options: {
list: [], // Start with empty list
// Add any custom options your component will use
},
components: {
input: DynamicListInput,
},
}Then create a custom input component that populates the list:
import {useState, useEffect} from 'react'
const DynamicListInput = (props) => {
const [listItems, setListItems] = useState([])
const {schemaType, renderDefault} = props
useEffect(() => {
// Your logic to populate list items
const items = [
{title: 'Option 1', value: 'option1'},
{title: 'Option 2', value: 'option2'},
]
setListItems(items)
}, [])
return renderDefault({
...props,
schemaType: {
...schemaType,
options: {...schemaType.options, list: listItems}
},
})
}Common Use Cases
1. Based on other field values - Use useFormValue to read other fields in the same document:
import {useFormValue} from 'sanity'
const RegionInput = (props) => {
const country = useFormValue(['country']) // Read 'country' field
const [regions, setRegions] = useState([])
useEffect(() => {
// Filter regions based on selected country
const filteredRegions = getRegionsForCountry(country)
setRegions(filteredRegions)
}, [country])
return props.renderDefault({
...props,
schemaType: {...props.schemaType, options: {...props.schemaType.options, list: regions}}
})
}2. From external APIs - The Asynchronous List Options Component recipe shows fetching from an API:
useEffect(() => {
fetch('https://api.example.com/options')
.then(res => res.json())
.then(data => setListItems(data))
}, [])3. From Sanity queries - Use useClient to query your content:
import {useClient} from 'sanity'
const QueryBasedList = (props) => {
const client = useClient({apiVersion: '2023-01-01'})
const [options, setOptions] = useState([])
useEffect(() => {
client.fetch('*[_type == "category"]{title, "value": slug.current}')
.then(setOptions)
}, [client])
return props.renderDefault({...props, schemaType: {...props.schemaType, options: {...props.schemaType.options, list: options}}})
}Important Notes
- The custom component approach uses
renderDefaultto render Sanity's default string input with your dynamic list - You can use any React hooks (
useState,useEffect,useFormValue,useClient, etc.) - This is currently the recommended pattern - there's an open feature request to support functions natively in
options.list, but using custom components is the official workaround
This pattern is quite flexible and handles most dynamic list scenarios you'll encounter!
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.