How to define a singleton page in Studio v3?

4 replies
Last updated: Dec 3, 2022
How to define a singleton page in Studio v3. Can anybody point me to an example?
AI Update

A singleton page in Studio v3 is a document type that can only have one instance, perfect for things like your home page, site settings, or an about page. You'll need to configure three things in your sanity.config.js file to make it work properly.

Here's a complete example:

import {defineConfig} from 'sanity'
import {structureTool} from 'sanity/structure'

// Define which types should be singletons
const singletonTypes = new Set(['settings', 'homePage'])

// Define which actions should be available for singletons
const singletonActions = new Set(['publish', 'discardChanges', 'restore'])

export default defineConfig({
  // ... other config
  plugins: [
    structureTool({
      structure: (S) =>
        S.list()
          .title('Content')
          .items([
            // Create a direct link to the singleton document
            S.listItem()
              .title('Settings')
              .id('settings')
              .child(
                S.document()
                  .schemaType('settings')
                  .documentId('settings')
              ),
            
            S.listItem()
              .title('Home Page')
              .id('homePage')
              .child(
                S.document()
                  .schemaType('homePage')
                  .documentId('homePage')
              ),
            
            // Add other document types normally
            ...S.documentTypeListItems().filter(
              (listItem) => !singletonTypes.has(listItem.getId())
            )
          ])
    })
  ],
  
  schema: {
    types: [
      // Your schema types here
    ],
    // Hide singletons from the "New document" menu
    templates: (templates) =>
      templates.filter(({schemaType}) => !singletonTypes.has(schemaType))
  },
  
  document: {
    // Remove unwanted actions for singletons (like delete/duplicate)
    actions: (input, context) =>
      singletonTypes.has(context.schemaType)
        ? input.filter(({action}) => action && singletonActions.has(action))
        : input
  }
})

Important: You need to create the document instance first before adding its type to the singletonTypes set. Otherwise, you won't have a way to create it through the UI.

The three key pieces are:

  1. Structure Builder - Creates a direct link to your singleton document without showing a list view
  2. Template filtering - Hides singletons from the "New document" menu so users can't create duplicates
  3. Document actions - Removes actions like "duplicate" and "delete" that could break your singleton

If you want an easier approach, check out the singleton-tools plugin which handles all this configuration automatically.

The full guide with more details is available in the Sanity singleton document guide.

Show original thread
4 replies
Granted it's been a minute since I ported my test studio over, but you would define the Structure in the same way as you would for V2.
Then, to control deletion/duplication/creation of new documents you'd add something like the following to your config:

document: {
      newDocumentOptions: (prev, context) =>
        prev.filter(document => !singleEdits.includes(document.templateId)),
      actions: (prev, { schemaType }) => {
        if (singleEdits.includes(schemaType)) {
          return prev.filter(prevAction => prevAction.action == 'publish');
        }
        return prev;
      },
    },
Note that my
singleEdits
is an array of document types.
Could you show me the config file so I see this in context?
Yep!
export default createConfig([
  {
    name: 'default',
    title: 'studioDaddy',
    projectId: 'k8p6uw8a',
    dataset: 'development',
    basePath: '/',
    plugins: [
      deskTool({
        name: 'sandbox',
        title: 'Sanbox',
        icon: MdOutlineBugReport,
        structure: sandbox,
      }),
    ],
    tools: [],
    schema: {
      types: schemaTypes,
      templates: [],
    },
    document: {
      newDocumentOptions: (prev, context) =>
        prev.filter(document => !singleEdits.includes(document.templateId)),
      actions: (prev, { schemaType }) => {
        if (singleEdits.includes(schemaType)) {
          return prev.filter(prevAction => prevAction.action == 'publish');
        }
        return prev;
      },
    },
  },
]);
As you probably noted, I am a total Sanity noobie 😉 I have this config file now but it does not change the
siteSettings
and
navigation
content types into Singletons. I can still create more copies. Any idea why?
import {defineConfig} from 'sanity'
import {deskTool} from 'sanity/desk'
import {schemaTypes} from './schemas'

const singleEdits = [
  { type: 'siteSettings' },
  { type: 'navigations' },
] 

export default defineConfig({
  name: 'default',
  title: 'ruhrpott-studio',
  projectId: '349a1vg2',
  dataset: 'production',
  plugins: [deskTool()],
  schema: {
    types: schemaTypes,
  },
  document: {
    newDocumentOptions: (prev, context) =>
      prev.filter(document => !singleEdits.includes(document.templateId)),
    actions: (prev, { schemaType }) => {
      if (singleEdits.includes(schemaType)) {
        return prev.filter(prevAction => prevAction.action == 'publish');
      }
      return prev;
    },
  },
});

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?