Dynamic string options from Sanity document list in schema

2 replies
Last updated: Jan 15, 2026
I have a requirement where the string options list should come from sanity. Like list of all the pages created and show it in options list below.
{
      name: 'goToPage',
      title: 'Go to page',
      description: 'This will be the page you would like the users to go to.',
      type: 'string',
      options: { list: PAGE_TYPES }, //This is where I want the data to come from sanity rather than pre-defined.
    },
AI Update

You'll need to create a custom input component to achieve dynamic options from Sanity. Unfortunately, you can't use async functions or queries directly in the options.list field of a schema definition - it only accepts static arrays.

Here's how to solve this with a custom input component:

Create a Custom Input Component

First, create a custom input component that fetches pages dynamically using the useClient hook:

// components/PageSelectInput.tsx
import { useClient } from 'sanity'
import { StringInputProps, set, unset } from 'sanity'
import { useEffect, useState } from 'react'
import { Stack, Select } from '@sanity/ui'

export function PageSelectInput(props: StringInputProps) {
  const { value, onChange } = props
  const client = useClient({ apiVersion: '2024-01-01' })
  const [pages, setPages] = useState<Array<{ _id: string; title: string }>>([])
  const [loading, setLoading] = useState(true)

  useEffect(() => {
    // Fetch all pages from Sanity
    client
      .fetch(`*[_type == "page"]{ _id, title, "slug": slug.current }`)
      .then((result) => {
        setPages(result)
        setLoading(false)
      })
      .catch((error) => {
        console.error('Error fetching pages:', error)
        setLoading(false)
      })
  }, [client])

  const handleChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
    const newValue = event.target.value
    onChange(newValue ? set(newValue) : unset())
  }

  return (
    <Stack space={3}>
      <Select
        value={value || ''}
        onChange={handleChange}
        disabled={loading}
      >
        <option value="">Select a page</option>
        {pages.map((page) => (
          <option key={page._id} value={page._id}>
            {page.title}
          </option>
        ))}
      </Select>
    </Stack>
  )
}

Use the Custom Component in Your Schema

Then reference this custom component in your schema:

import { PageSelectInput } from './components/PageSelectInput'

{
  name: 'goToPage',
  title: 'Go to page',
  description: 'This will be the page you would like the users to go to.',
  type: 'string',
  components: {
    input: PageSelectInput
  }
}

Alternative: Use a Reference Field

If you're storing page IDs anyway, consider using Sanity's built-in reference field instead:

{
  name: 'goToPage',
  title: 'Go to page',
  type: 'reference',
  to: [{ type: 'page' }],
  options: {
    filter: '_type == "page"' // Optional: add custom filters
  }
}

This gives you a searchable dropdown with all pages automatically, without needing a custom component. The reference field also ensures data integrity and lets you easily resolve the full page document when querying.

The custom input approach gives you more control over the UI and what values are stored (like storing slugs instead of IDs), while the reference approach is simpler and more maintainable for typical use cases where you're linking to other documents.

Show original thread
2 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.

Was this answer helpful?