Auto-generate readOnly/hidden field value from another field in Sanity

12 replies
Last updated: Dec 1, 2025
Is there a way to automatically set a readOnly or hidden field? I was hoping that I could create a slug field that would be generated from a number field using the
source
and
slufigy
options but it doesn't get created automatically set to a value upon publish.
AI Update

The slug field's source and slugify options don't automatically generate the slug value on publish - they only power the "Generate" button in the Studio UI. However, you can automatically set a slug (or any readOnly/hidden field) on publish using a custom document action.

Here's the correct way to implement this in Studio v3:

Create a Custom Publish Action

First, create your custom action component (e.g., actions/SetSlugAndPublish.tsx):

import { useDocumentOperation } from 'sanity'
import { useState, useEffect } from 'react'
import type { DocumentActionComponent } from 'sanity'

function generateSlug(text: string | number): string {
  return String(text)
    .toLowerCase()
    .replace(/\s+/g, '-')
    .replace(/[^\w-]+/g, '')
}

export const SetSlugAndPublishAction: DocumentActionComponent = (props) => {
  const { patch, publish } = useDocumentOperation(props.id, props.type)
  const [isPublishing, setIsPublishing] = useState(false)

  useEffect(() => {
    // Reset publishing state if publish completes
    if (isPublishing && !publish.disabled) {
      setIsPublishing(false)
    }
  }, [isPublishing, publish.disabled])

  return {
    label: isPublishing ? 'Publishing...' : 'Publish',
    disabled: publish.disabled || isPublishing,
    onHandle: () => {
      setIsPublishing(true)
      
      // Generate slug from your source field(s)
      // For a number field:
      const slug = generateSlug(props.draft?.yourNumberField || '')
      
      // Patch the document with the slug
      patch.execute([{ set: { slug: { _type: 'slug', current: slug } } }])
      
      // Then publish
      publish.execute()
      
      props.onComplete()
    }
  }
}

Register the Action in Your Config

In your sanity.config.ts (or .js):

import { defineConfig } from 'sanity'
import { SetSlugAndPublishAction } from './actions/SetSlugAndPublish'

export default defineConfig({
  // ... other config
  document: {
    actions: (prev, context) => {
      // Replace the default publish action for specific document types
      if (context.schemaType === 'yourDocumentType') {
        return prev.map((originalAction) => 
          originalAction.action === 'publish' 
            ? SetSlugAndPublishAction 
            : originalAction
        )
      }
      return prev
    }
  }
})

Key Points for Studio v3

Important differences from older patterns:

  1. Import from sanity package: Use import { useDocumentOperation } from 'sanity' - NOT from @sanity/react-hooks (deprecated)

  2. Return an action description object: Your component must return an object with label, onHandle, and optionally disabled, icon, shortcut, etc. - NOT a React component

  3. Use the callback pattern: In defineConfig, use document.actions: (prev, context) => [...] to access context and modify the actions array

  4. For multiple source fields: You can combine fields like this:

    const slug = generateSlug(`${props.draft?.field1} ${props.draft?.field2}`)
  5. For number fields specifically: Just convert to string:

    const slug = generateSlug(String(props.draft?.yourNumberField))

Alternative: Using Initial Value Templates

If you want the slug set when the document is created (not on publish), you can use Initial Value Templates instead:

import { defineConfig } from 'sanity'

export default defineConfig({
  // ... other config
  schema: {
    templates: (prev) => [
      ...prev,
      {
        id: 'document-with-slug',
        title: 'Document with Slug',
        schemaType: 'yourDocumentType',
        value: (params) => ({
          slug: {
            _type: 'slug',
            current: generateSlug(params.yourNumberField || 'default')
          }
        })
      }
    ]
  }
})

This approach works great for hidden or readOnly fields that should be set automatically based on other field values when publishing or creating documents.

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