Typical use cases for Structure builder

An overview of typical use cases for structure builder, with code examples.

You can use Structure builder to customize how documents are grouped and listed in the Sanity Studio. In this article, you find some common, but minimal use cases with complete code examples. You may want to work through the introduction guide, or the overview article first. For more in-depth documentation of the methods in Structure builder, check out the reference documentation.

Singletons and “one-of” documents

Often you want to restrict a document type to having just one document. This is typically some sort of global configuration, or information that's singular, such as your company’s contact information. When you add a document type, the Studio will per default allow people to create multiple documents of that type. You can use the Structure builder API to limit a document type to a limited set of document(s) with predefined id(s).

This approach is also future proofing your content model, because if the time comes where you need to have multiple configurations, or you need add contact information for your new branch, you can always “break out” of the singleton by reconfiguring the structure.

Let's look at an example:

import S from "@sanity/desk-tool/structure-builder";
 
export default () =>
  S.list()
    .title('Content')
    .items([
      S.listItem()
        .title('Config')
        .child(
          S.document()
            .schemaType('siteSettings')
            .documentId('siteSettings')
        ),
      // Add a visual divider (optional)
      S.divider(),
      // List out the rest of the document types, but filter out the config type
      ...S.documentTypeListItems()
        .filter(listItem => !['siteSettings'].includes(listItem.getId()))
    ])

This definition will produce the following document structure:

The portfolio studio from sanity.io/create (Open full image)

Override the default publishing actions

When you create “one of” or “some of” documents, you most often also want to restrict what publishing actions can be done with those documents. Typically you only want to allow for updating and publishing new document changes. For now, use the experimental action UI affordances in the schema definition to define which actions should be allowed:

// /schemas/documents/siteSettings.js

export default {
  name: 'siteSettings',
  type: 'document',
  title: 'Site settings',
__experimental_actions: [/*'create',*/ 'update', /*'delete',*/ 'publish'],
fields: [ /* the fields */ ] }

This will remove siteSettings from the “Create new…” menus, and remove the delete button from the action bar.

The additional actions are disabled for the site settings document type. (Open full image)

Manual grouping of documents

When a project matures, the content model tends to grow with many document types. At some point you probably want to organize these document types differently than listing them out in the root pane. With the Structure builder API you can override the default lists and add your own list items and define what goes into them.

Adding additional site setting documents

Building on the previous example, let's say you create another website, and want to use the same project and dataset to manage content there as well. You can add another settings document in your structure definition by adding an S.listItem in the top list, and in its child, ad a new S.list that contains the “siteSettings” item, as well as your new “anotherSiteSettings” item.

import S from "@sanity/desk-tool/structure-builder";
 
export default () =>
  S.list()
    .title('Content')
    .items([
S.listItem()
.title('Configs')
.child(
S.list()
.title('Configs')
.items([
S.document()
.schemaType('siteSettings')
.documentId('siteSettings'),
S.document()
.schemaType('siteSettings')
.documentId('anotherSiteSettings'),
])
),
// Add a visual divider (optional) S.divider(), // List out the rest of the document types, but filter out the config type ...S.documentTypeListItems() .filter(listItem => !['siteSettings'].includes(listItem.getId())) ])

Dynamic grouping of documents

It's often useful to group documents automatically by some field value, or a combination of field values. Common examples is grouping documents based by authors, publishing date periodes, editorial status, by categories, or even by the dominant background color in a document’s main image.

This example shows how you can list documents by the category documents they reference.

import S from '@sanity/desk-tool/structure-builder'

export default () =>
  S.list()
    .title('Content')
    .items([
      // List out all the document types in schema.js
      ...S.documentTypeListItems(),
      // Add a new list item for projects by category
      S.listItem()
        .title('Projects by category')
        .child(
          // List out all categories
          S.documentTypeList('category')
            .title('Projects by category')
            .child(catId =>
              // List out project documents where the _id for the selected
              // category appear as a _ref in the project’s categories array
              S.documentList()
                .schemaType('sampleProject')
                .title('Projects')
                .filter(
                  '_type == "sampleProject" && $catId in categories[]._ref'
                )
                .params({ catId })
            )
        ),
    ])

The rationale here is that we first list out the available category documents, and then use their id (called catId in the configuration) in a GROQ filter to generate a list of sampleProject documents.

Gotcha

The .filter() function (that is, method) used in this example, is not the Array.prototype.filter function that you may be used to from JavaScript elsewhere. It is method connected to S.documentList() that returns documents from a GROQ-filter.

Tabs with content previews

The Structure builder API also gives you control over how a document node is presented within a collapsible pane. More specifically, it lets you set up one to multiple Views that either returns the default form, or a component. A View receive a collection of props, including the values for the document in different states: draft, published, historical, currently displayed (e.g. if you have selected a specific document revision).

Structure builder comes with a function for defining the default tabs for document views. In this example we will use it to make a simple JSON preview for all document types where another view hasn't been set in a structure definition.

import React from 'react'
import S from '@sanity/desk-tool/structure-builder'

const JsonPreview = ({document}) => (
  <pre>{JSON.stringify(document.displayed, null, 2)}</pre>
)

export const getDefaultDocumentNode = () => 
  S.document().views([
    S.view.form(),
    S.view.component(JsonPreview).title('JSON')
  ])

export default S.defaults()

Was this article helpful?