Schemas define the structure of your content in Sanity Studio. They’re written in JavaScript or TypeScript and use the defineType and defineField helpers to create type-safe definitions.
Understanding schema types
Sanity has three main categories of schema types:
Document types : Top-level content types like posts, authors, or products
Object types : Reusable field groups that can be embedded in documents
Field types : Individual fields like strings, numbers, images, or arrays
Creating your first schema
Import the schema helpers
Start by importing defineType and defineField from the sanity package:
import { defineType , defineField } from 'sanity'
Create a basic document type with required metadata:
export default defineType ({
name: 'author' ,
type: 'document' ,
title: 'Author' ,
icon: UserIcon ,
description: 'This represents an author' ,
fields: [
// fields go here
]
})
Add fields to your document
Define the fields that make up your document:
fields : [
defineField ({
name: 'name' ,
title: 'Name' ,
type: 'string' ,
description: 'This is the name of the author' ,
validation : ( rule ) => rule . required (),
}),
{
name: 'image' ,
title: 'Image' ,
type: 'image' ,
options: { hotspot: true },
},
{
name: 'role' ,
title: 'Role' ,
type: 'string' ,
options: {
list: [
{ value: 'developer' , title: 'Developer' },
{ value: 'designer' , title: 'Designer' },
{ value: 'ops' , title: 'Operations' },
],
},
},
]
Define how documents appear in lists and search results:
preview : {
select : {
title : 'name' ,
media : 'image' ,
role : 'role' ,
},
prepare ({ title , media , role }) {
return {
title: title ,
media: media ,
subtitle: role ? AUTHOR_ROLES . find ( option => option . value === role )?. title : undefined ,
}
},
}
Field types reference
Sanity provides a rich set of built-in field types:
Primitive types
Media types
Relational types
fields : [
{ name: 'title' , type: 'string' , title: 'Title' },
{ name: 'count' , type: 'number' , title: 'Count' },
{ name: 'published' , type: 'boolean' , title: 'Published' },
{ name: 'publishedAt' , type: 'datetime' , title: 'Published at' },
{ name: 'content' , type: 'text' , title: 'Content' },
]
fields : [
{
name: 'coverImage' ,
type: 'image' ,
title: 'Cover Image' ,
options: { hotspot: true },
},
{
name: 'attachment' ,
type: 'file' ,
title: 'Attachment' ,
},
]
fields : [
{
name: 'author' ,
title: 'Author' ,
type: 'reference' ,
to: { type: 'author' },
},
{
name: 'categories' ,
title: 'Categories' ,
type: 'array' ,
of: [
{
type: 'reference' ,
to: { type: 'category' },
},
],
},
]
Creating reusable object types
Object types are reusable field groups that can be embedded in multiple documents:
export const myObject = defineType ({
type: 'object' ,
name: 'myObject' ,
title: 'My object' ,
fields: [
{
name: 'first' ,
type: 'string' ,
title: 'First' ,
},
{
name: 'second' ,
type: 'string' ,
title: 'Second' ,
},
{
name: 'image' ,
type: 'image' ,
title: 'Image' ,
},
],
})
Then use it in your documents:
fields : [
{
name: 'metadata' ,
type: 'myObject' ,
title: 'Metadata' ,
},
]
Arrays and complex structures
Arrays can contain primitives, objects, or references:
fields : [
// Array of strings
{
name: 'tags' ,
title: 'Tags' ,
type: 'array' ,
of: [{ type: 'string' }],
},
// Array of objects
{
name: 'reviews' ,
type: 'array' ,
of: [
{
type: 'object' ,
name: 'review' ,
fields: [
{
name: 'title' ,
title: 'Title' ,
type: 'string' ,
validation : ( rule ) => rule . required (),
},
{
name: 'rating' ,
title: 'Rating' ,
type: 'number' ,
},
],
},
],
},
]
Validation rules
Add validation to ensure data quality:
defineField ({
name: 'title' ,
title: 'Title' ,
type: 'string' ,
validation : ( rule ) => rule . required (). min ( 5 ). max ( 100 ),
})
Validation rules are chainable. You can combine multiple rules like .required().min(10).max(200).regex(/^[A-Z]/)
Initial values
Set default values for new documents:
export default defineType ({
name: 'book' ,
type: 'document' ,
fields: [ ... ] ,
initialValue: {
title: 'Untitled' ,
publicationYear: new Date (). getFullYear (),
} ,
})
Field options and customization
Many field types support options for customization:
{
name : 'genre' ,
title : 'Genre' ,
type : 'string' ,
options : {
list : [
{ title: 'Fiction' , value: 'fiction' },
{ title: 'Non Fiction' , value: 'nonfiction' },
{ title: 'Poetry' , value: 'poetry' },
],
layout : 'radio' , // or 'dropdown'
direction : 'horizontal' , // or 'vertical'
},
}
{
name : 'coverImage' ,
title : 'Cover Image' ,
type : 'image' ,
options : {
hotspot : true , // Enable image cropping
metadata : [ 'blurhash' , 'lqip' , 'palette' ],
},
}
Organizing fields with fieldsets
Group related fields together:
export default defineType ({
name: 'product' ,
type: 'document' ,
fieldsets: [
{
name: 'pricing' ,
title: 'Pricing' ,
options: { collapsible: true , collapsed: false },
},
] ,
fields: [
{
name: 'price' ,
type: 'number' ,
fieldset: 'pricing' ,
},
{
name: 'currency' ,
type: 'string' ,
fieldset: 'pricing' ,
},
] ,
})
Adding your schema to the studio
Register your schema types in your studio configuration:
import { defineConfig } from 'sanity'
import author from './schemas/author'
import book from './schemas/book'
export default defineConfig ({
// ... project settings
schema: {
types: [ author , book ],
} ,
})
Schema changes are hot-reloaded in development, so you’ll see updates immediately in the studio.
Next steps