Patching documents in Sanity client components

6 replies
Last updated: Nov 30, 2025
Hey all, is there anyway to patch a document within a client component in sanity? I've looked on github and the docs but couldnt find anything, I guess I need to use sanity/migrate ? This seems to be more for cli use
AI Update

You can definitely patch documents from a client component! You don't need sanity/migrate for this - that's specifically for CLI-based content migrations. Here's what you need to know:

Using the Sanity Client to Patch Documents

You can use @sanity/client to patch documents from any JavaScript environment, including React client components. Here's a basic example:

import {createClient} from '@sanity/client'

const client = createClient({
  projectId: 'your-project-id',
  dataset: 'your-dataset-name',
  useCdn: false, // Important: set to false for mutations
  apiVersion: '2025-02-06',
  token: process.env.SANITY_API_TOKEN // Required for mutations
})

// Patch a document
async function updateDocument(documentId, newTitle) {
  const result = await client
    .patch(documentId)
    .set({title: newTitle})
    .commit()
  
  return result
}

The patch API supports various operations like set, unset, setIfMissing, insert, inc, and more.

Important Security Consideration

⚠️ Never expose write tokens in client-side code! This is a critical security issue. If you put a token with write permissions directly in your browser code, anyone can inspect your code and use that token to modify or delete your content.

The Safe Approach: Use Server-Side API Routes

Instead of patching directly from a client component, create a server-side API endpoint that handles the mutation:

// app/api/update-document/route.js (Next.js App Router example)
import {createClient} from '@sanity/client'

const client = createClient({
  projectId: process.env.NEXT_PUBLIC_SANITY_PROJECT_ID,
  dataset: process.env.NEXT_PUBLIC_SANITY_DATASET,
  useCdn: false,
  apiVersion: '2025-02-06',
  token: process.env.SANITY_API_TOKEN // Safely stored server-side
})

export async function POST(request) {
  const {documentId, updates} = await request.json()
  
  // Add validation and authorization checks here!
  
  const result = await client
    .patch(documentId)
    .set(updates)
    .commit()
  
  return Response.json(result)
}

Then call this from your client component:

// Your client component
async function handleUpdate(documentId, newData) {
  const response = await fetch('/api/update-document', {
    method: 'POST',
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify({documentId, updates: newData})
  })
  
  return response.json()
}

If You're Building a Studio Plugin

If you're working within Sanity Studio (like in a custom input component), you don't use the client directly. Instead, you use the onChange callback with PatchEvent:

import {set, unset} from 'sanity'

function MyCustomInput(props) {
  const {value, onChange} = props
  
  const handleChange = (newValue) => {
    onChange(newValue ? set(newValue) : unset())
  }
  
  return <input value={value} onChange={(e) => handleChange(e.target.value)} />
}

Summary

  • ✅ Use @sanity/client with .patch() for programmatic updates
  • Never expose write tokens in client-side code
  • ✅ Create server-side API routes to safely handle mutations
  • ✅ Use PatchEvent with set/unset when building Studio plugins
  • ℹ️ sanity/migrate is for CLI-based content migrations, not runtime updates

The key takeaway: while you can technically use the client in a browser component, you should always proxy mutations through a secure server-side endpoint to protect your API tokens.

Show original thread
6 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.

Was this answer helpful?