Schema design: Let editors choose from multiple source fields or custom value
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!
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
- Keep source data separate: The
readOnly: trueonsourceDataprevents accidental edits - Use Sanity UI components: The
@sanity/uilibrary (Radio, Card, TextInput) keeps your component looking native - Access document context: Use
props.documentto read other fields (like your source data) - Handle patches properly: Use the
set()helper from thesanitypackage as shown in the custom input components guide
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 thread8 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.