Initial Value Templates

Define an initial value for a new document created in Sanity Studio.

Initial Value Templates allow you to define (as the name implies) templates for the values of a new document's fields. These values are only applied when you create a new document – the values are not retroactively applied to existing documents.

There are two ways of defining initial values, depending on what you want to achieve. 1) You can define a single set of inital values to apply to all new documents, or 2) define a set of different templates to choose from when creating a new document. Let's have a look at each of them:

Define a single set of initial values

If you always want a particular document type to have a single set of initial values, the easiest way to do this is by specifying the initialValue property on a document type. In the following example, the project schema type is given the value false for the isHighlighted field.

export default {
  name: 'project',
  type: 'document',
  title: 'Project',
  fields: [
    {
      name: 'title',
      title: 'Title',
      type: 'string'
    },
    {
      name: 'isHighlighted',
      title: 'Highlighted',
      type: 'boolean'
    },
    {
      name: 'releaseDate',
      title: 'Release date',
      type: 'datetime'
    }
  ],
  initialValue: {
    isHighlighted: false
  }
}

Sometimes you may want to compute some property values. For instance, in the example above, you may want to set the releaseDate property to be the current date. You can do this by specifying a function for the initialValue property:

export default {
  // ...
  initialValue: () => ({
    isHighlighted: false,
releaseDate: (new Date()).toISOString()
}) }

Define multiple templates

So far we’ve only looked at customizing the initial value for all documents of a document type. Quite often you'll want to provide a set of different templates that an editor can choose from.

Defining the part

In order to customize the list of available templates you’ll have to implement it as a part. In your Sanity Studio’s sanity.json (at the root of the project), you have an array named parts. Your schema is most probably already an entry in this list.

In the same way, we’ll define a part for the templates list. The definition looks like this:

{
  "name": "part:@sanity/base/initial-value-templates",
  "path": "./initialValueTemplates.js"
}

In this example, the definition of the templates is located at the root of the project folder, but you can name it and place it wherever you want. Just make sure to get the path correct.

If you had the studio running locally while doing this, you should restart the process.

Defining the templates

The definition file (initialValueTemplates.js) we pointed at above should export an array of templates. We'll go into detail about what the definition looks like in a bit, but for now, let's start by creating an empty javascript file in the referenced location (in the example above, <sanity-studio>/initialValueTemplates.js).

Next, you will want to import a module to help you define the templates.

import T from '@sanity/base/initial-value-template-builder'

export default [
  ...T.defaults()  
]

We’ve now recreated the exact same functionality that you get out of the box. This gives you an idea of what happens behind the scenes: each document type will generate a template for it’s type, which will produce either an empty object as its initial value, or whichever value is set in the schema type definitions initialValue property.

It’s helpful to note that the T.defaults() call will return an array of the templates, and that you can use standard JavaScript array primitives to filter or modify these templates should you wish.

Now, onto something more useful: defining a custom initial values template.

import T from '@sanity/base/initial-value-template-builder'

export default [
  ...T.defaults(),
  
  T.template({
    id: 'author-developer',
    title: 'Developer',
    schemaType: 'author',
    value: {
      role: 'developer'
    }
  })
]

In the above example, we’re now including all the default templates, as well as a specific template we have named Developer. When creating new documents, you should now get the option to create either an Author (the default template for the author type) or a Developer, which is the specific template we defined above. The value property can also be defined as a function.

Often you’ll want to assign a custom icon for templates to distinguish them from the base schema type. You can do this by setting the icon property:

import T from '@sanity/base/initial-value-template-builder'
import CodeIcon from 'react-icons/lib/md/code'
export default [ ...T.defaults(), T.template({ id: 'author-developer', title: 'Developer', schemaType: 'author',
icon: CodeIcon,
value: { role: 'developer' } }) ]

Inserting objects or references as default values

Fields which are object types (or is a reference to another document) have a few rules that you need to keep in mind:

  • Objects need to include the _type property, indicating which schema type they are pointing to. For instance, if inserting a geopoint, you will set the field value as:
    {_type: 'geopoint', lat: 59.92409, lng: 10.7584}
  • References are expressed using an object with a _ref property. This means that if you want to use a reference as a default value, you'll want to express the value as:
    {_type: 'reference', _ref: 'document-id-to-reference'}
  • Images and files are represented as objects with a nested asset field (which is a reference to the actual asset document). Combining the two points above, the default value for an image field would look something like this:
    {_type: 'image', asset: {_type: 'reference', _ref: 'image-someId-200x300-jpg'}}
  • While objects in arrays should generally have a _key property, the initial value system will generate the _key property if it is not included in the provided value (using a randomly generated string).

Resolving initial values asyncronously

The initial value can also be specified as an asyncronous function (a function returning a promise). This allows exciting things like running a request to an API to get data needed for the initial value. For instance:

import axios from 'axios'

export default {
  // ...
  initialValue: async () => {
    const response = await axios.get('https://api.sanity.io/pets')
    return {favoritePetName: response.data[0].name}
  }
}

Parameterized templates

A common use case is to populate fields based on a set of parameters. You can do this by defining a parameters array for your template. By defining the value property as a function, you'll get the parameters passed to the template as the argument to the function. Each item in the parameters array follows the same declaration as fields within a schema object type:

import T from '@sanity/base/initial-value-template-builder'

export default [
  ...T.defaults(),

  T.template({
    id: 'authorWithRole',
    title: 'Author with role',
    schemaType: 'author',
    parameters: [
      {
        name: 'roleName',
        title: 'Role name',
        type: 'string'
      }
    ],
    value: parameters => ({
      role: parameters.roleName
    })
  })
]

Using templates in a structure

When defining your own structure for the desk tool (using the Structure Builder), a common use case is to create filtered lists of documents. In these cases you probably want the “new document” action to not start with a totally empty document, but have values prepopulated which matches the structure the user is currently in. That was very abstract. Let’s be a little more concrete:

In a dataset containing a large number of books and their related authors, you may want to segment the books by author. So you create a desk structure that looks something along the lines of this:

S.list()
  .id('root')
  .title('Content')
  .items([
    S.listItem({
      id: 'books-by-author',
      title: 'Books by author',
      schemaType: 'book',
      child: () =>
        S.documentTypeList('author').child(authorId =>
          S.documentTypeList('book')
            .title('Books by author')
            .filter('_type == $type && author._ref == $authorId')
            .params({type: 'book', authorId})
        )
    }),
    ...S.documentTypeListItems()
  ])

This satisfies the navigation part, but it doesn’t select the author we want when creating a new document. For this to work, we need to first create a parameterized initial value template, then tell the structure to use it. Let’s first define the initial value template. In your initialValueTemplates.js:

T.template({
  id: 'book-by-author',
  title: 'Book by author',
  description: 'Book by a specific author',
  schemaType: 'book',
  parameters: [{name: 'authorId', type: 'string'}],
  value: params => ({
    author: {_type: 'reference', _ref: params.authorId}
  })
})

Sweet. Now let’s use it in our structure:

S.list()
  .id('root')
  .title('Content')
  .items([
    S.listItem({
      id: 'books-by-author',
      title: 'Books by author',
      schemaType: 'book',
      child: () =>
        S.documentTypeList('author').child(authorId =>
          S.documentTypeList('book')
            .title('Books by author')
            .filter('_type == $type && author._ref == $authorId')
            .params({type: 'book', authorId})
.initialValueTemplates([
S.initialValueTemplateItem('book-by-author', {authorId})
])
) }), ...S.documentTypeListItems() ])

Note how we’re defining which initial value templates should be valid in this context: By specifying just a single template, that is the only template valid in this context and will thus be used. If you specify multiple templates, you will get a choice of which template to use. The second argument to S.initialValueTemplateItem() is a set of parameters – in this case, we’re passing the authorId from the parent pane as a parameter.

Configuring the “new document” menu

How to access the new document menu

Sanity allows you to define the items that should appear in the new document menu in a similar way to how you define a structure for the desk tool. You start by creating a javascript file somewhere in your studio folder, for instance newDocumentStructure.js.

The file needs to export an array of items that should appear in the dialog. A simple start would be to just use the default set of items:

import S from '@sanity/base/structure-builder'

export default [
  ...S.defaultInitialValueTemplateItems()
]

We now need to tell Sanity that this file exists and what role it plays. To do so, we open sanity.json and find the parts array. We'll want to add a new item to it:

{
  "name": "part:@sanity/base/new-document-structure",
  "path": "./newDocumentStructure.js"
}  

Gotcha

After saving the file, you will need to restart the development server (we know, we’re working on it).

Once that’s up and running, you should see the exact same list of items as you did previously. Not too exciting, is it? Well, let’s say you have a template that takes a role parameter and prepopulates a field based on that. Let’s add some new items to the start of the list, each with a different role:

import S from '@sanity/base/structure-builder'

const personTemplateId = 'personWithRole'
const roles = [
  {name: 'developer', title: 'Developer'},
  {name: 'designer', title: 'Designer'},
  {name: 'admin', title: 'Administrator'},
  {name: 'manager', title: 'Manager'}
]

export default [
  ...roles.map(role =>
    S.initialValueTemplateItem(personTemplateId, {role: role.name})
      .id(`personRole-${role.name}`)
      .title(role.title)
  ),
  
  ...S.defaultInitialValueTemplateItems()
]

We’ve now got some new shortcuts for creating a person document with a prefilled role. The S.initialValueTemplateItem() method returns a builder that can be used to customize the title, icon and similar, should you want to make the items slightly more distinct.

New document menu with initial value templates for person