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 in the Studio – the values are not retroactively applied to existing documents or when documents are created from the client libraries.
Gotcha
Initial values can currently only be applied to documents upon creating the document in the Studio. Trying to apply initial values to other types e.g. an object when creating an array item will not work.
There are two ways of defining initial values, depending on what you want to achieve. 1) You can define a single set of initial 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:
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()
})
}
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.
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.
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 its 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 { MdCode } from 'react-icons/md'
export default [
...T.defaults(),
T.template({
id: 'author-developer',
title: 'Developer',
schemaType: 'author',
icon: MdCode,
value: {
role: 'developer'
}
})
]
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 ageopoint
, 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).
The initial value can also be specified as an asynchronous 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}
}
}
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
})
})
]
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.
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.