How to set document actions in Sanity Studio v3?

4 replies
Last updated: Oct 4, 2022
Hi!
I'm testing out the new Studio v3, and I'm trying to change the
__experimental_actions__: ["update", "publish"]
field that's suggested to use when you're creating site settings for instance. How do I emulate the same behavior in V3?
AI Update

In Studio v3, the __experimental_actions property has been replaced with a more robust configuration system. Here's how to achieve the same behavior for singleton documents like site settings:

Using document.actions in Studio v3

In your sanity.config.ts (or .js), use the document.actions configuration to filter which actions appear on documents:

import {defineConfig} from 'sanity'

export default defineConfig({
  // ... other config
  document: {
    // Filter document actions
    actions: (prev, context) => {
      // For singleton documents like site settings
      if (context.schemaType === 'siteSettings') {
        // Only allow publish action (equivalent to ["update", "publish"])
        return prev.filter(({action}) => action === 'publish')
      }
      return prev
    },
    
    // Prevent creating new documents of this type
    newDocumentOptions: (prev, context) => {
      return prev.filter(
        (template) => template.templateId !== 'siteSettings'
      )
    }
  }
})

What Changed from v2 to v3

The v3 approach provides more flexibility and control:

  • document.actions: Replaces __experimental_actions for filtering which actions (publish, delete, duplicate, etc.) appear on documents. You receive the full list of default actions and can filter or reorder them based on schemaType or other context.

  • document.newDocumentOptions: Controls what appears in the "Create new document" menu, preventing users from creating multiple instances of singleton documents.

Complete Singleton Pattern

Here's a complete example that properly restricts a singleton document:

// Array of document types that should be singletons
const singletonTypes = ['siteSettings', 'homePage']

export default defineConfig({
  // ... other config
  document: {
    actions: (prev, context) => {
      if (singletonTypes.includes(context.schemaType)) {
        // Only allow publish action for singletons
        return prev.filter(({action}) => action === 'publish')
      }
      return prev
    },
    
    newDocumentOptions: (prev, context) => {
      // Remove singletons from "Create new" menu
      return prev.filter(
        (template) => !singletonTypes.includes(template.templateId)
      )
    }
  }
})

Additional Benefits

The v3 approach gives you access to more context, allowing you to:

  • Filter actions based on user roles (context.currentUser)
  • Customize behavior per document type
  • Add custom actions alongside default ones
  • Reorder actions in the toolbar

You can also combine this with Structure Builder to create dedicated menu items that always open your singleton documents, making them feel like true settings pages rather than document lists.

Show original thread
4 replies
If its still experimental, I’m out 😅
Should be a core feature to create singleton items!
As I understand it
__experimental_actions__
is depreciated.Instead you override how the New Document and Action buttons appear in your
sanity.config.ts
.Here is how I did it.

//sanity.config.ts
import {createConfig, DocumentBadgeComponent, DocumentBadgesResolver, isDev} from 'sanity'
import {deskTool, StructureBuilder} from 'sanity/desk'
import {schemaTypes} from './schemas'
import {visionTool} from '@sanity/vision'
import {media} from 'sanity-plugin-media'
import {structure, defaultDocumentNode} from './structure/structure'

import {CustomPublishAction} from './actions/CustomPublishAction'
import {HelloWorldBadge} from './actions/HelloWorldBadge'

import { colorInput } from "@sanity/color-input";

const devOnlyPlugins = [ visionTool() ]

// array of document types that only publishing should be allowed on.
// makes no sence to have 'create' or 'duplicate'
export const publishOnlyDocuments = ['homePage', 'siteSettings']


// Determins the actions that appear in the Publish bar
const actions = (actions: any, {schemaType}: any) => {
  // deconstruct all actions so we can order them if required.
  const [publish, discardChanges, unPublish, duplicate, deleteDocument, ...anyOtherActions] =
    actions

  // if this document is in the  publishOnlyDocuments then we don't want
  // return only publish (or schedule if it is there)
  if (publishOnlyDocuments.includes(schemaType)) {
    return [publish]
  }

  return [
    CustomPublishAction, // Example action which intercepts the current publish action.
    discardChanges,
    unPublish,
    duplicate,
    deleteDocument,
    ...anyOtherActions,
  ]
}

// Determins the Document badges that appear in the Publish bar.
const badges = (badges: any) => {
  const [...otherBadges] = badges
  return [...otherBadges, HelloWorldBadge]
}


// Determins the New Documents Types that appear when clicking the New Document Button . 
const newDocumentOptions = (newDocumentOptions: any) => {  
  const filteredNewDocumentOptions = newDocumentOptions.filter((documentOption: any) => {
    // return only the documentTypes that are not publishOnlyDocuments
    return !publishOnlyDocuments.includes(documentOption.templateId)
  })
  return filteredNewDocumentOptions
}


export default createConfig({
  name: 'default',
  title: 'WHATEVER',
  projectId: 'WHATEVER',
  dataset: 'production',

  document: {
    actions: actions,
    badges: badges,
    newDocumentOptions: newDocumentOptions,
  },

  plugins: [
    deskTool({
      structure,
      defaultDocumentNode,
    }),
    // media(),
    colorInput(),
    ...(isDev ? devOnlyPlugins : []),
  ],

  schema: {
    types: schemaTypes,
  },
})
Hey
user U
! You'll want to ask V3 questions here until general release.
Oh okay, thanks 🙂 Will do.

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?