Custom component: secure way to update nested array items without token
You're absolutely right to be concerned about exposing auth tokens in client-side code! The good news is you don't need a proxy server for this use case. Your custom input component runs inside the authenticated Sanity Studio environment, which already handles authentication through the user's login session.
The Core Issue
You shouldn't be using a separate sanityClient with auth tokens inside custom input components. The Studio already has authentication, and custom components communicate changes through the onChange prop that's passed to your component.
The Correct Approach for Studio v3
Your custom input component receives onChange and value props from Sanity. To make updates, you use the onChange callback with patch operations like set() and unset().
Import the Required Functions
import {set, unset} from 'sanity'Note: In Studio v3, you import set and unset directly from 'sanity', not from @sanity/form-builder (that was Studio v2). There's no separate PatchEvent class in v3 - you just pass the patch operations directly to onChange.
Basic Pattern for Updates
export function SectionItemList(props) {
const {onChange, value = []} = props
// In v3, onChange accepts patch operations directly
const handleChange = (newValue) => {
onChange(newValue ? set(newValue) : unset())
}
}Updating Label or StartTime
To update fields within a specific array item, you work with the entire value array:
const updateLabel = (key, newLabel) => {
const updatedSections = value.map(section =>
section._key === key
? {...section, label: newLabel}
: section
)
onChange(set(updatedSections))
}
const updateStartTime = (key, newTime) => {
const updatedSections = value.map(section =>
section._key === key
? {...section, startTime: newTime}
: section
)
onChange(set(updatedSections))
}Creating New Workout Groups
To add a new item, generate a unique _key for it:
import {randomKey} from '@sanity/util/content'
const addNewWorkoutGroup = () => {
const newGroup = {
_type: 'workoutgroup',
_key: randomKey(12),
label: 'New Workout',
startTime: '',
exercises: []
}
const updatedSections = [...value, newGroup]
onChange(set(updatedSections))
}Updating the Exercises Array
For nested arrays, you update the parent array with the modified nested structure:
// Update entire exercises array for a specific workout group
const updateExercises = (workoutGroupKey, newExercises) => {
const updatedSections = value.map(section =>
section._key === workoutGroupKey
? {...section, exercises: newExercises}
: section
)
onChange(set(updatedSections))
}
// Add a single exercise to a workout group
const addExercise = (workoutGroupKey, newExercise) => {
const exerciseWithKey = {
...newExercise,
_key: randomKey(12),
_type: 'workoutgroupexercise'
}
const updatedSections = value.map(section =>
section._key === workoutGroupKey
? {
...section,
exercises: [...(section.exercises || []), exerciseWithKey]
}
: section
)
onChange(set(updatedSections))
}Removing Items
const removeWorkoutGroup = (key) => {
const updatedSections = value.filter(section => section._key !== key)
onChange(
updatedSections.length > 0
? set(updatedSections)
: unset()
)
}Schema Configuration
Your schema should use the components.input property for Studio v3:
{
name: 'sections',
title: 'Sections',
type: 'array',
components: {
input: SectionItemList // Studio v3 syntax
},
of: [
{
type: 'workoutgroup',
weak: true,
},
],
}Note: If you were using inputComponent (camelCase), that was Studio v2 syntax. Studio v3 uses the components object.
Why This Works Securely
- No exposed tokens: Changes go through the Studio's internal APIs using the logged-in user's session
- Real-time collaboration: This approach maintains Studio's real-time features
- Proper validation: Updates go through Studio's validation system
- User permissions: Changes respect the current user's permission level
Why NOT a Proxy Server
Your proxy server idea would technically work, but it's unnecessary complexity that would:
- Break real-time collaboration features
- Add latency with an extra network hop
- Require maintaining additional infrastructure
- Lose Studio features like undo/redo and presence indicators
The onChange pattern is the standard approach for custom input components and keeps everything secure within the authenticated Studio environment. Your custom component has full access to make changes through onChange without needing any additional authentication.
Show original thread8 replies
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.