
Grab your gear: The official Sanity swag store
Read Grab your gear: The official Sanity swag storeYes, absolutely! This is a great use case for a custom input component with useFormValue. Here's how to build it:
Your custom input component needs to:
useFormValue to watch the meal fielduseEffectonChange with set() and unset() to patch the Content LakeHere's a complete example for your shopping list scenario:
import { useFormValue, set, unset } from 'sanity'
import { useEffect, useState, useCallback } from 'react'
import { Stack, Checkbox, Card } from '@sanity/ui'
export const ShoppingListInput = (props) => {
const { onChange, value = [] } = props
const mealRef = useFormValue(['meal']) // Watch the meal field
const [ingredients, setIngredients] = useState([])
// Fetch ingredients when meal changes
useEffect(() => {
if (mealRef?._ref) {
// Query Sanity for the meal's ingredients
client.fetch(
`*[_id == $mealId][0].ingredients`,
{ mealId: mealRef._ref }
).then(setIngredients)
} else {
setIngredients([])
}
}, [mealRef?._ref]) // Re-run when meal reference changes
const handleCheckboxChange = useCallback((ingredient, checked) => {
let newValue
if (checked) {
// Add ingredient to array
newValue = [...value, ingredient]
} else {
// Remove ingredient from array
newValue = value.filter(item => item._key !== ingredient._key)
}
// Patch to Content Lake - correct v3 pattern
onChange(newValue.length > 0 ? set(newValue) : unset())
}, [onChange, value])
return (
<Stack space={3}>
{ingredients.map((ingredient) => (
<Card key={ingredient._key} padding={2}>
<Checkbox
checked={value.some(v => v._key === ingredient._key)}
onChange={(e) => handleCheckboxChange(ingredient, e.target.checked)}
>
{ingredient.name}
</Checkbox>
</Card>
))}
</Stack>
)
}1. The useFormValue hook watches other fields in your document. Pass it an array path like ['meal'] for a top-level field, or ['parent', 'child'] for nested fields.
2. The dependency array in useEffect should include mealRef?._ref (not just mealRef) to trigger updates when the actual reference changes, not just the object identity.
3. Patching with onChange (Studio v3): As shown in the official guide, you pass the result of set() or unset() directly to onChange():
onChange(nextValue ? set(nextValue) : unset())Important: In Studio v3, you do NOT use PatchEvent.from() - that was the v2 pattern. The v3 API is simpler and cleaner.
4. Performance optimization: Wrap your change handler in useCallback with onChange and value in the dependency array to prevent unnecessary re-renders.
In your schema, attach the component like this:
{
name: 'shoppingList',
type: 'array',
of: [{ type: 'reference', to: [{ type: 'ingredient' }] }],
components: {
input: ShoppingListInput
}
}useFormValue only works inside custom input components, not in document actions or other contexts_key for proper React rendering and array patchingset and unset are imported from the 'sanity' package in v3This pattern works great for dependent fields, conditional inputs, and dynamic lists based on other selections!
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.
Content operations
Content backend


The only platform powering content operations
By Industry


Tecovas strengthens their customer connections
Build and Share

Grab your gear: The official Sanity swag store
Read Grab your gear: The official Sanity swag store