How AI is powering better personalization in e-commerce [with Vercel]. Sign up now

Schema design: Let editors choose from multiple source fields or custom value

8 repliesLast updated: Nov 29, 2025

Hi everyone! I’m trying to design a schema that will accept data from a source system and allow our content editors to make a choice as to which piece of data to show on our front-end website.
For example, I have a document type called Project. In our source system, a project can have a short name, a long name and an alias. For each project in the source system, I want to push all three fields into each Project document in Sanity to be read-only and have the content editors decide which version of the project name will be published to the website.
If none of the above meet the requirements, then they can choose an “Other” field that can be selected.

Attached is a sketch of what I had in my head from a UI perspective. Has anyone tried something similar? I’m presuming it’ll require a custom input component. Any tips for making this as simple as possible from a schema/UI perspective? Thanks!
AI Update

Your use case is definitely achievable and yes, you'll want a custom input component! This is a great pattern for handling data from external systems while giving editors control over what gets published.

Schema Structure

First, set up your schema to store all the data:

{
  name: 'project',
  type: 'document',
  fields: [
    {
      name: 'sourceData',
      type: 'object',
      readOnly: true, // Lock these fields from manual editing
      fields: [
        {name: 'shortName', type: 'string'},
        {name: 'longName', type: 'string'},
        {name: 'alias', type: 'string'}
      ]
    },
    {
      name: 'displayName',
      type: 'object',
      components: {
        input: ProjectNameSelector // Your custom component
      },
      fields: [
        {
          name: 'selected',
          type: 'string',
          options: {
            list: ['shortName', 'longName', 'alias', 'custom']
          }
        },
        {
          name: 'customValue',
          type: 'string'
        }
      ]
    }
  ]
}

Custom Input Component

Based on the custom input components documentation, here's a simplified approach:

import {set, unset} from 'sanity'
import {Stack, Radio, TextInput, Card, Text} from '@sanity/ui'

export function ProjectNameSelector(props) {
  const {value, onChange, elementProps} = props
  const sourceData = props.document?.sourceData || {}
  
  const handleSelectionChange = (selection) => {
    onChange(set({
      selected: selection,
      customValue: value?.customValue
    }))
  }
  
  const handleCustomChange = (event) => {
    onChange(set({
      selected: 'custom',
      customValue: event.target.value
    }))
  }
  
  return (
    <Stack space={3}>
      <Radio
        checked={value?.selected === 'shortName'}
        onChange={() => handleSelectionChange('shortName')}
      >
        <Text>Short Name: {sourceData.shortName}</Text>
      </Radio>
      
      <Radio
        checked={value?.selected === 'longName'}
        onChange={() => handleSelectionChange('longName')}
      >
        <Text>Long Name: {sourceData.longName}</Text>
      </Radio>
      
      <Radio
        checked={value?.selected === 'alias'}
        onChange={() => handleSelectionChange('alias')}
      >
        <Text>Alias: {sourceData.alias}</Text>
      </Radio>
      
      <Card padding={3} border>
        <Stack space={2}>
          <Radio
            checked={value?.selected === 'custom'}
            onChange={() => handleSelectionChange('custom')}
          >
            <Text>Custom</Text>
          </Radio>
          {value?.selected === 'custom' && (
            <TextInput
              value={value?.customValue || ''}
              onChange={handleCustomChange}
            />
          )}
        </Stack>
      </Card>
    </Stack>
  )
}

Querying the Final Value

When querying for your frontend, you can use GROQ to resolve the selected value:

*[_type == "project"] {
  displayName: select(
    displayName.selected == "shortName" => sourceData.shortName,
    displayName.selected == "longName" => sourceData.longName,
    displayName.selected == "alias" => sourceData.alias,
    displayName.selected == "custom" => displayName.customValue
  )
}

Tips

This pattern works really well for systems-of-record scenarios where you want source data preserved but give editors control over presentation. You could even enhance it further by showing a preview of what the selected name looks like in your frontend design!

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

Related contributions