How to sync user documents on studio load with custom desk structure?

14 replies
Last updated: Jan 28, 2026
Hi! I'm trying to create a document for all users like this: https://www.sanity.io/schemas/create-a-document-for-all-current-project-users-68b6f0db but I really want it to try and sync each time the studio loads. Any ideas on how to go about this?
I've tried doing this:
https://www.sanity.io/schemas/custom-default-desk-pane-35148d61 but it doesn't load with a custom desk structure. I've also tried making my desk structure into a React component but that doesn't work.
AI Update

I understand you want to automatically create user documents for all project members each time the Studio loads. Unfortunately, the links you referenced appear to be broken or moved, but I can help you solve this problem.

The challenge you're facing is that certain hooks (like the one that might create initial documents) don't work with custom desk structures. Here are a few approaches to solve this:

Solution 1: Use a Studio Plugin with Layout Component

Instead of relying on desk structure lifecycle, create a Studio plugin that runs initialization logic when the Studio mounts:

// plugins/userDocumentSync.js
import {definePlugin} from 'sanity'
import {useEffect} from 'react'

export const userDocumentSync = definePlugin({
  name: 'user-document-sync',
  studio: {
    components: {
      layout: (props) => {
        useEffect(() => {
          // Fetch project members and sync user documents
          syncUserDocuments()
        }, [])
        
        return props.renderDefault(props)
      }
    }
  }
})

async function syncUserDocuments() {
  const client = sanityClient.withConfig({apiVersion: '2024-01-01'})
  
  // Fetch project members using Management API
  const members = await fetch(
    `https://api.sanity.io/v2021-06-07/projects/${projectId}/members`,
    {headers: {Authorization: `Bearer ${token}`}}
  ).then(res => res.json())
  
  // Create/update documents for each member
  for (const member of members) {
    await client.createIfNotExists({
      _id: `user.${member.id}`,
      _type: 'user',
      name: member.displayName,
      email: member.email
    })
  }
}

Then add this plugin to your sanity.config.js:

import {userDocumentSync} from './plugins/userDocumentSync'

export default defineConfig({
  // ...
  plugins: [
    // ... other plugins
    userDocumentSync()
  ]
})

For a more robust solution that doesn't depend on Studio loading, use Sanity Functions with a scheduled trigger:

// sanity.blueprint.ts
import {defineBlueprint} from '@sanity/blueprint'

export default defineBlueprint({
  functions: [
    {
      name: 'sync-user-documents',
      runtime: 'node22',
      schedule: '0 * * * *', // Run hourly
      handler: async (event) => {
        // Fetch members from Management API
        // Sync to dataset
      }
    }
  ]
})

This approach is better because:

  • It runs independently of Studio
  • Works even when no one has the Studio open
  • More reliable and scalable
  • Can be triggered on schedules or via webhooks

Solution 3: Tool Menu Action

Add a manual sync action to the Studio's tool menu that users can trigger when needed:

// tools/syncUsers.js
import {definePlugin} from 'sanity'

export const syncUsersTool = definePlugin({
  name: 'sync-users-tool',
  tools: [
    {
      name: 'sync-users',
      title: 'Sync Users',
      component: SyncUsersComponent
    }
  ]
})

Key Points

  • Custom desk structures bypass some Studio lifecycle hooks, which is why your approach wasn't working
  • Sanity Functions are the modern, recommended approach for automation like this - they run independently of Studio and can be triggered on schedules or events
  • You'll need to use the Sanity Management API to fetch project members at https://api.sanity.io/v2021-06-07/projects/{projectId}/members
  • Consider using createIfNotExists() to avoid duplicates
  • The plugin layout component approach (Solution 1) will work for your specific "sync on Studio load" requirement, but Functions (Solution 2) provide a more reliable solution

The plugin with layout component wrapper is your best bet for the "run every time Studio loads" requirement with a custom desk structure. It works by wrapping the entire Studio layout rather than hooking into the desk structure specifically.

Show original thread
14 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?