Patching documents in Sanity client components

6 replies
Last updated: Aug 14, 2024
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
I tried
sanity/migrate
and yeah this doesnt look like it will work. The only other idea I have is to use the HTTP API. Has anyone done mutations within the studio before?
The problem here is that the API key is needed for authentication, it would mean making the API key public on the client side which isnt ideal
ohhhh cool:
ohh cool:
await fetch(      `https://${process.env.NEXT_PUBLIC_SANITY_PROJECT_ID}.<http://api.sanity.io/v2021-06-07/data/mutate/${process.env.NEXT_PUBLIC_SANITY_DATASET}|api.sanity.io/v2021-06-07/data/mutate/${process.env.NEXT_PUBLIC_SANITY_DATASET}>`,

      {
        method: 'POST',
        mode: 'cors',
        credentials: 'include',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(titleMutation),
      },
    )
Dont need to send auth tokens 🤘
Awesome!

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?