Understanding custom input components for complex objects in Sanity.io

1 replies
Last updated: Apr 14, 2020
Hello people of the sanity community! I am trying to get my head around custom input components used on an field of type ‘object’. This is the object in question, I have it as a part of my Product document.

export default {
    title: 'Product Info',
    name: 'productInfo',
    type: 'object',
    fields: [
        {
            title: 'Product Id',
            name: 'productId',
            type: 'number'
        },
        {
            title: 'Product Storage Id',
            name: 'storageId',
            type: 'number'
        },
        {
            title: 'hasAttributes',
            name: 'hasAttributes',
            type: 'boolean'
        },
        
    ]
}
The object has some more attributes but they aren’t needed for this example, so I have a custom input component that prepopulates the product names and respective values with information from our PIM, in order for the user to select a product name from a dropdown and you have all the values you want to set like this as an object:

{ productId: 90922, storageId: 29992, hasAttributes: true }
So my question is, how would I use the PatchEvent set() specified in the documentation for an more complex object than a single string or number? If I provided an object would it be able to map the keys to the fieldnames in the schema? Below is provided of the example from the documentation with the single number.

const createPatchFrom = value => PatchEvent.from(value === '' ? unset() : set(Number(value)))
AI Update

Great question! Yes, you can absolutely pass an object to set() when working with object-type fields, and Sanity will map the keys to the field names in your schema automatically.

For your productInfo object, here's how to handle it in Studio v3 (the API has changed from v2):

import {set, unset} from 'sanity'

export const MyCustomInput = (props) => {
  const {onChange, value} = props
  
  const handleProductSelect = (selectedProduct) => {
    const productData = {
      productId: selectedProduct.productId,
      storageId: selectedProduct.storageId,
      hasAttributes: selectedProduct.hasAttributes
    }
    
    // Pass the object directly to set(), then to onChange
    onChange(productData ? set(productData) : unset())
  }
  
  // ... rest of your component with dropdown, etc.
}

The key difference from the docs example you referenced is that you're passing an entire object instead of a single value. The keys in your object (productId, storageId, hasAttributes) will automatically map to the corresponding field names in your productInfo schema.

Here's a complete example with a dropdown that prepopulates from your PIM:

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

export const ProductSelector = (props) => {
  const {onChange, value} = props
  
  // Your PIM products data
  const products = [
    {id: 1, name: 'Product A', productId: 90922, storageId: 29992, hasAttributes: true},
    {id: 2, name: 'Product B', productId: 90923, storageId: 29993, hasAttributes: false},
    // ... more products
  ]
  
  const handleChange = useCallback((event) => {
    const selectedId = event.currentTarget.value
    
    if (!selectedId) {
      onChange(unset())
      return
    }
    
    const selectedProduct = products.find(p => p.id === parseInt(selectedId))
    
    if (selectedProduct) {
      onChange(set({
        productId: selectedProduct.productId,
        storageId: selectedProduct.storageId,
        hasAttributes: selectedProduct.hasAttributes
      }))
    }
  }, [onChange])
  
  return (
    <Stack space={2}>
      <Select onChange={handleChange} value={value?.productId || ''}>
        <option value="">Select a product</option>
        {products.map(product => (
          <option key={product.id} value={product.id}>
            {product.name}
          </option>
        ))}
      </Select>
    </Stack>
  )
}

Important note about Studio v3: The old PatchEvent.from() API from Studio v2 is no longer used. In v3, you import set and unset directly from sanity and pass them straight to onChange(), as shown in the official Studio v3 custom input guide.

The object you pass to set() will merge with the existing field structure, and Sanity handles all the real-time patching to the Content Lake for you!

For anyone having the same problem as I: I figured out that patches from custom input and the http api probably worked the same under the hood. So by looking at the documentation here, https://www.sanity.io/docs/http-patches , I made this work by sending in an object with keys as the field names and values as the new values that I wanted.

user Y
or someone at Sanity, Id love to help extend the documentation for https://www.sanity.io/docs/custom-input-widgets to cover objects as well! Let me know if you want me to write something up that you can look through, edit and publish!

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?