How to generate massive amounts of demo content for Sanity
It can be useful for testing plugins, front ends, or other integrations to have a Sanity Studio populated with fake content.
Go to How to generate massive amounts of demo content for SanityThe promise of an excellent Sanity Studio is that Developers craft uniquely useful experiences for Authors.
This guide covers how to customise some of the most commonly used parts of the Studio on a per-user or per-role basis.
An Administrator in your Studio likely needs visibility across all of your content types. But an Author that is only concerned with a specific domain could be given a simpler experience with less options.
With Desk Structure configured in your Studio, we can subscribe to the userStore
in order to find out details about the current user.
The example below will display different items to a user whose roles include one with the name
"administrator".
// ./sanity.config.ts
import {visionTool} from '@sanity/vision'
import {defineConfig} from 'sanity'
import {deskTool, StructureBuilder} from 'sanity/desk'
import {schemaTypes} from './src/schemas'
const authorItems = (S: StructureBuilder) => [
S.documentTypeListItem('post').title('Posts'),
S.documentTypeListItem('author').title('Authors'),
]
const adminItems = (S: StructureBuilder) => [
S.documentTypeListItem('project').title('Projects'),
S.documentTypeListItem('setting').title('Settings'),
]
export default defineConfig({
// ...other config options
plugins: [
// ...other plugins
deskTool({
structure: (S, {currentUser}) => {
const isAdmin = currentUser?.roles.find((role) => role.name === 'administrator')
const title = isAdmin ? 'Administrator Content' : 'Content'
const items = isAdmin ? [...authorItems(S), S.divider(), ...adminItems(S)] : authorItems(S)
return S.list().title(title).items(items)
},
}),
],
})
In addition to customizing which Desk Structure items are shown, we can customize what items are shown in a Document List based on GROQ's identity()
function.
The GROQ function identity()
returns the ID of the User performing the query. Super-useful in the Studio!
For example, perhaps instead of listing all author
documents, you only want the Author to see and edit their own document.
S.listItem()
.id('profile')
.title('My Profile')
.child(() =>
S.documentList()
.title('My Profile')
.schemaType('author')
.filter('_type == "author" && userId == identity()')
),
However, this will only work if your author
documents contain a correct value in a userId
field, let's fix that!
Hiding documents in Desk Structure will not remove them from search results in the Studio or in Reference fields. Apply additional filtering to those where possible.
Adding Sanity User ID's to documents can make them easier to query with GROQ using identity()
Using Initial Value Templates you can create individual templates to populate new documents with static or dynamic values.
Using a similar function to the one above, we can create a template for post
type documents to pre-fill an author
field with the User ID.
// ./sanity.config.ts
export default defineConfig({
// ... all other config
schema: {
// ... schema types, etc
templates: (prev, {currentUser}) => {
// User is not guaranteed to be logged in
if (!currentUser?.id) {
return prev
}
// Create new template for new author type documents
const authorWithUserId = {
id: `author-with-user-id`,
title: `Author with User ID`,
schemaType: `author`,
value: {
userId: currentUser.id,
},
}
// Remove the default template for author type documents
const prevFiltered = prev.filter((template) => template.id !== `author`)
return [...prevFiltered, authorWithUserId]
},
}
})
author
field readOnly
so the data is not corruptedIt is possible, and likely, your schema will include something like an author
type document. Or a greater abstraction; a person
type document that can be assigned the value of "author" either as a string
or reference
.
These are different from Sanity Users – also known as Project Members – and that's fine! An author
document likely contains much more details about that person than their Sanity User object does – like biography, contact and social details.
So instead of adding a User ID to every article
, page
, etc. Add it to a single author
document which is connected to those types by a reference.
This way the User ID is only assigned to a single document, and a query like this will resolve all of their connected documents based on who is querying them:
*[_type in ["article", "page"] && author->userId == identity()]
An exception to this is you need to lock an entire document at the Permissions level based on a User ID, as Content Resources cannot resolve references. See the section on Permissions below.
While we have hidden items of the Desk Structure from specific Users, they will still be able to create new documents of all types from the Navbar (next to the search box).
In the section above, we customized Initial Value Templates to remove the default new author
document template. Here we can ensure globally that non-administrators cannot create new project
or setting
type documents.
// ./sanity.config.ts
export default defineConfig({
// ... all other config
document: {
// ... all other document config
newDocumentOptions: (prev, {currentUser}) => {
// User is not guaranteed to be logged in
if (!currentUser?.id) {
return prev
}
const isAdmin = currentUser?.roles.find((role) => role.name === 'administrator')
// Administrators can create any document type
if (isAdmin) {
return prev
}
// Remove the option to create new 'project' or 'setting' type documents
const prevFiltered = prev.filter(
(template) => !['project', 'setting'].includes(template.templateId)
)
return prevFiltered
},
},
})
Fortunately the hidden
and readOnly
properties in schema have direct access to a currentUser
variable.
Here we're hiding a price
field to any user without the administrator
role.
defineField({
name: 'price',
type: 'number',
// Hide the price field from non-administrators
hidden: ({currentUser}) => !currentUser?.roles.find((role) => role.name === 'administrator'),
})
readOnly
has the same currentUser
property, and it can even be added at the document level to set all fields to readOnly
.
In this example, we'll lock the document to any user that is not an administrator
or editor
.
export default defineType({
name: 'project',
title: 'Project',
type: 'document',
readOnly: ({currentUser}) => !currentUser?.roles.find((role) => role.name === 'administrator'),
fields: [ // ...all your fields ]
})
Setting a field hidden
will not remove its value from the Inspect panel, so Users will still be able to see the value of the field.
Making fields readOnly
will only protect the field in the Studio, a sufficiently motivated User could still write to the field with an API call.
The only true way to secure these fields and documents is with access control.
Sanity's Roles and Permissions API will ensure that built-in Document Actions like Publish or Delete cannot be clicked by Users that lack those permissions.
But for your custom Actions, you'll need to inspect the current user to hide or disable the Action selectively.
Fortunately, the Studio provides a useCurrentUser
hook, which can be used inside a Document Action as they are rendered inside of React scope.
Here's a code example that disables the action for non-administrators.
import {DocumentActionProps, useCurrentUser} from 'sanity'
export default function CreateMilestone(props: DocumentActionProps) {
const user = useCurrentUser()
const isAdmin = user ? user.roles.find((role) => role.name === 'administrator') : false
return {
// Disable the button for non-"administrator" users
disabled: !isAdmin,
label: 'Create Milestone',
onHandle: async () => {
// ...your function logic
props.onComplete()
},
}
}
You could also:
null
to hide the action from non-administrators orsanity.config.ts
in the document.actions
configurationOn Enterprise plans, access to view and edit documents in the Studio experience can be automatically be customized thanks to Content Resources.
Going back to our userId
the example earlier, it's possible to write a rule that would only give access to users editing an author
document other than the one with their userId
with the following rule:
_type == "article" && userId == identity()
Because Content Resources cannot dereference fields, this userId
would also need to be present on any document type you wish to restrict access to in the same way.
The same logic can filter document lists, search and reference fields at the permission level. By restricting view access to documents that do not have your userId
.
All Published documents are viewable in a Public Dataset. If you're trying to restrict the ability to View documents using Permissions, you must also switch to a Private Dataset.
Configuring every part of the Studio into a unique experience for specific user groups allows you to place gentle guardrails around authors to ensure they have the most guided and pleasant experience possible.
Sanity Composable Content Cloud is the headless CMS that gives you (and your team) a content backend to drive websites and applications with modern tooling. It offers a real-time editing environment for content creators that’s easy to configure but designed to be customized with JavaScript and React when needed. With the hosted document store, you query content freely and easily integrate with any framework or data source to distribute and enrich content.
Sanity scales from weekend projects to enterprise needs and is used by companies like Puma, AT&T, Burger King, Tata, and Figma.
It can be useful for testing plugins, front ends, or other integrations to have a Sanity Studio populated with fake content.
Go to How to generate massive amounts of demo content for SanityAdd live as-you-type preview to any presentational component
Go to Create a reusable live preview componentPreview draft content and changes as-you-type in Next.js from Sanity Studio
Go to Live Preview with Next.js App Router and Sanity StudioSummarise form progression by decorating the entire editing form for a document with a component loaded at the root level.
Go to Create a document form progress component