Conditionally set field values based on another field in Sanity

2 replies
Last updated: May 2, 2022
Is there a way to conditionally set the value of a field based on the value of another field?
In detail, what I want is to present a dropdown to the user with nice human-readable
colorTheme
names. I would also like to have to hidden string fields called
textColor
and
backgroundColor
, and I would like to set the value of these fields based on the value of the
colorTheme
field. Then the user would be able to select a color field, and the API response would be something like
{
  "colorTheme": "forest",
  "textColor": "#fefefe",
  "backgroundColor": "#179e40"
}
AI Update

Yes, you can conditionally set field values based on another field's value! For your specific use case with colorTheme, textColor, and backgroundColor, the best approach is to create a custom input component at the object level that wraps all three fields together.

Here's how to implement it:

Schema Setup

First, define your fields as an object type in your schema:

{
  name: 'themeSettings',
  type: 'object',
  components: {
    input: ColorThemeInput  // Apply custom component at object level
  },
  fields: [
    {name: 'colorTheme', type: 'string'},
    {name: 'textColor', type: 'string', hidden: true},
    {name: 'backgroundColor', type: 'string', hidden: true}
  ]
}

Custom Input Component

Then create your custom input component. The key insight from the Sanity documentation is that when patching object fields, you can pass an object with keys matching your field names:

import {set, unset} from 'sanity'
import {Stack, Select} from '@sanity/ui'

const colorThemes = {
  forest: {textColor: '#fefefe', backgroundColor: '#179e40'},
  ocean: {textColor: '#ffffff', backgroundColor: '#0077be'},
  sunset: {textColor: '#000000', backgroundColor: '#ff6b35'}
}

function ColorThemeInput(props) {
  const {onChange, value} = props
  
  const handleChange = (event) => {
    const themeName = event.target.value
    
    if (!themeName) {
      onChange(unset())
      return
    }
    
    const theme = colorThemes[themeName]
    
    // Pass an object with keys matching field names
    onChange(set({
      colorTheme: themeName,
      textColor: theme.textColor,
      backgroundColor: theme.backgroundColor
    }))
  }
  
  return (
    <Stack space={3}>
      <Select value={value?.colorTheme || ''} onChange={handleChange}>
        <option value="">Select a theme</option>
        {Object.keys(colorThemes).map(key => (
          <option key={key} value={key}>
            {key.charAt(0).toUpperCase() + key.slice(1)}
          </option>
        ))}
      </Select>
    </Stack>
  )
}

How It Works

The critical piece is that when you apply a custom input component to an object type field, the onChange function can update all child fields at once by passing an object where the keys match your field names. This is documented in the PatchEvent article, which explains that patches work similarly to the HTTP PATCH API.

When the user selects "forest" from the dropdown, your API response will be:

{
  "colorTheme": "forest",
  "textColor": "#fefefe",
  "backgroundColor": "#179e40"
}

Alternative: Document-Level Logic

If you need this behavior across multiple documents or want to keep the logic separate from the UI, you could also use Sanity Functions with a document change handler to automatically update the color fields whenever colorTheme changes. However, the custom input component approach gives you immediate feedback in the Studio and is simpler for this use case.

The key takeaway: apply your custom component at the object level, not at individual field levels, when you need to update multiple related fields together.

Show original thread
2 replies
You can do this using a custom input component for the select that patches the current document and some hidden fields, see https://www.sanity.io/docs/custom-input-widgets#1f69be67cfe0 .
But I’d rather put all these values in an object with a custom input component.


{
  name: 'theme',
  title: 'Theme',
  type: 'object',
  inputComponent: ThemeSelect,
  options: {
     themes: [{ name: 'forest', textColor: '#fefefe', backgroundColor: '#179e40' }]
  }
  fields: [
    {
      name: 'textColor',
      title: 'Text color',      
      type: 'string'
    },
    {
      name: 'backgroundColor',
      title: 'Background color',      
      type: 'string'
    }
  ]
}
I’d use
Sanity UI to add a select box in the custom component.

https://www.sanity.io/docs/custom-input-widgets

https://www.sanity.io/guides/asynchronous-list-options
Thanks, this looks like a good way to solve it!

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?