Is it possible to dynamically populate the values in a List for a string type?

3 replies
Last updated: Jul 7, 2020
is it possible to dynamically populate the values in a list for a
string
type?
AI Update

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 renderDefault to 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!

just following up on this. Is it possible to populate the values of a list at runtime or can it only be done at build time?
actually, I guess one way around it is if I build a custom component that fetches those values at runtime. That would make more sense.
The built in component can't only do it at build time, but as you say, for dynamic values, a custom component is the way to go. Some resources that might be helpful:

https://github.com/sanity-io/sanity-recipes
https://github.com/sanity-io/sanity/tree/next/packages/example-studio/components

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.

Was this answer helpful?