
Sanity Schema Types: A Complete Reference
Sanity schema types are the building blocks you use to describe content in Sanity Studio. The built-in types fall into four groups: primitive types for single values (string, text, number, boolean, date, datetime, url, email), structured types with special behaviour (slug, image, file, geopoint), relationship types for linking content (reference, plus cross-dataset and global document references), and container types that hold other fields (document, object, array, block and span). Every field you define uses one of these types, or a custom type you've built from them.
Choosing the right type affects the input editors see, the JSON stored in the Content Lake and how easily you can query it. This reference covers every built-in type with its purpose, its most useful options, what it stores and a working example. Use it alongside how to create your first schema when you're deciding which type a field should be.
Quick Reference Table
| Type | Use it for | Stored as |
|---|---|---|
string | Short single-line text, titles, enums | String |
text | Multi-line plain text | String |
number | Prices, quantities, ratings | Number |
boolean | On/off flags | true or false |
date | Calendar dates without time | "YYYY-MM-DD" string |
datetime | Timestamps | ISO 8601 string in UTC |
url | Links | String |
email | Email addresses | String |
slug | URL segments | Object with current |
image | Images with hotspot and crop | Object with an asset reference |
file | PDFs, videos, downloads | Object with an asset reference |
geopoint | Map coordinates | Object with lat, lng, alt |
reference | Links to other documents | Object with _ref |
object | Groups of fields | Object |
array | Lists of values or objects | Array |
block | Rich text paragraphs | Portable Text block |
document | Top-level content types | Document with _id, _type and system fields |
Fields Every Type Shares
Every field, whatever its type, accepts these common properties:
- name: the key used in the stored JSON. Required.
- type: the schema type. Required.
- title: the label shown in Studio.
- description: help text under the label.
- hidden: a boolean or a function that hides the field conditionally.
- readOnly: a boolean or a function that stops editing.
- initialValue: a default value, or a function that returns one.
- validation: a function that receives a
ruleand returns rules. - deprecated: an object with a
reason, shown to editors while you phase a field out.
Primitive Types
string
A single line of text. Use options.list to turn it into a dropdown or radio buttons:
defineField({
name: 'status',
type: 'string',
options: {
list: [
{ title: 'Draft idea', value: 'idea' },
{ title: 'In progress', value: 'progress' },
{ title: 'Ready', value: 'ready' },
],
layout: 'radio',
direction: 'horizontal',
},
initialValue: 'idea',
}),
Without options.list, it's a normal text input. With a list, the stored value is the value, not the title, so you can rename labels later without touching data.
text
Multi-line plain text with no formatting. rows sets the height of the box:
defineField({ name: 'excerpt', type: 'text', rows: 3 }),
Use text for summaries, meta descriptions and notes. If editors need bold, links or headings, use an array of block instead.
number
Stores integers or decimals. Combine with validation for business rules:
defineField({
name: 'price',
type: 'number',
validation: (rule) => rule.required().positive().precision(2),
}),
Like string, it accepts options.list for fixed choices, such as a rating from 1 to 5.
boolean
A toggle switch by default. Set options.layout to 'checkbox' for a checkbox. Give it an initialValue, because an unset boolean is undefined, not false, and that matters in GROQ filters:
defineField({ name: 'featured', type: 'boolean', initialValue: false }),
date and datetime
date stores a calendar date such as "2026-11-04". datetime stores a full timestamp in UTC, such as "2026-11-04T09:30:00.000Z", while showing it in the editor's local time. Both accept display options:
defineField({
name: 'eventDate',
type: 'date',
options: { dateFormat: 'DD/MM/YYYY' },
}),
defineField({
name: 'publishedAt',
type: 'datetime',
options: { dateFormat: 'DD/MM/YYYY', timeFormat: 'HH:mm', timeStep: 15 },
}),
The format options only change how the input displays dates. The stored value is always ISO format, which sorts correctly in GROQ.
url and email
Both are strings with built-in format checking. url checks for a valid URL, and you can tighten the allowed schemes with validation:
defineField({
name: 'website',
type: 'url',
validation: (rule) => rule.uri({ scheme: ['https', 'mailto'] }),
}),
defineField({ name: 'contactEmail', type: 'email' }),
Structured Types
slug
A URL-safe identifier with a Generate button that builds the value from another field:
defineField({
name: 'slug',
type: 'slug',
options: { source: 'title', maxLength: 96 },
validation: (rule) => rule.required(),
}),
It stores an object, not a string:
{ "_type": "slug", "current": "my-first-post" }
So you query it as slug.current. The guide to custom slugs in Sanity covers custom slugify functions and uniqueness checks.
image
Uploads an image to the asset pipeline and stores a reference to the asset. Set hotspot: true so editors can choose the focal point and crop, and add fields for alt text or captions:
defineField({
name: 'mainImage',
type: 'image',
options: { hotspot: true },
fields: [
defineField({ name: 'alt', type: 'string', title: 'Alternative text' }),
],
}),
Stored data:
{
"_type": "image",
"asset": { "_type": "reference", "_ref": "image-a1b2c3...-1200x800-jpg" },
"hotspot": { "x": 0.5, "y": 0.4, "height": 0.6, "width": 0.6 },
"alt": "A red bicycle against a brick wall"
}
The asset document holds the URL and metadata such as dimensions. Read working with images in Sanity for the full story.
file
Like image, but for any file type. Use options.accept to restrict what editors can upload:
defineField({
name: 'brochure',
type: 'file',
options: { accept: '.pdf' },
}),
Query the URL with brochure.asset->url.
geopoint
Stores a location as latitude, longitude and optional altitude:
{ "_type": "geopoint", "lat": -33.8688, "lng": 151.2093, "alt": 0 }
Out of the box, editors type the coordinates. For a map picker, add a plugin such as the Google Maps input. GROQ can filter geopoints with the geo:: functions.
Relationship Types
reference
Links one document to another by ID. to lists the allowed target types:
defineField({
name: 'author',
type: 'reference',
to: [{ type: 'author' }],
}),
Stored as { "_type": "reference", "_ref": "<document id>" }, and dereferenced in GROQ with ->. Useful extras include weak: true for references that allow the target to be deleted, and options.filter to limit which documents appear in the picker. See how to use references in Sanity schemas.
Cross-Dataset and Global Document References
Two advanced variants link beyond the current dataset:
- Cross-dataset references: point to documents in another dataset in the same project, for example a shared
productsdataset used by several sites. - globalDocumentReference: points to documents in other resources, such as another dataset or the Media Library, identified by
resourceTypeandresourceId.
These are aimed at larger, multi-dataset setups, so check your plan and the official documentation before relying on them.
Container Types
object
Groups fields together. You can define it inline:
defineField({
name: 'seo',
type: 'object',
fields: [
defineField({ name: 'metaTitle', type: 'string' }),
defineField({ name: 'metaDescription', type: 'text', rows: 2 }),
],
}),
Or register it as a named type and use type: 'seo' anywhere. Objects are the basis of reusable building blocks, covered in building reusable object types.
array
Holds a list of items. The of property lists which types are allowed:
defineField({
name: 'tags',
type: 'array',
of: [defineArrayMember({ type: 'string' })],
options: { layout: 'tags' },
}),
defineField({
name: 'gallery',
type: 'array',
of: [defineArrayMember({ type: 'image', options: { hotspot: true } })],
options: { layout: 'grid' },
}),
Arrays of objects get a unique _key on each item, which Studio uses to track edits. You can mix several types in one array, which is how page builders work. The arrays guide covers layouts, sorting and limits.
block and span
block is the type for rich text. You almost always use it inside an array:
defineField({
name: 'body',
type: 'array',
of: [
defineArrayMember({
type: 'block',
styles: [
{ title: 'Normal', value: 'normal' },
{ title: 'Heading 2', value: 'h2' },
{ title: 'Quote', value: 'blockquote' },
],
marks: {
decorators: [
{ title: 'Bold', value: 'strong' },
{ title: 'Italic', value: 'em' },
],
},
}),
defineArrayMember({ type: 'image' }),
],
}),
Each block contains span children holding the text and its marks. You rarely define span yourself, but you'll see it in the stored data. The result is Portable Text, explained in what is Portable Text.
document
The top-level type editors create and publish. Every document gets system fields automatically:
| Field | Meaning |
|---|---|
_id | Unique ID, prefixed with drafts. for drafts |
_type | The schema type name |
_createdAt | When the document was created |
_updatedAt | When it was last changed |
_rev | Revision ID, changes with every edit |
You can query all of these in GROQ, for example sorting by _updatedAt.
Choosing the Right Type
A few rules of thumb:
- Short label or enum:
string, withoptions.listfor fixed choices. - Plain paragraph:
text. Formatted content: array ofblock. - Something editors might reuse across documents: a separate
documenttype plus areference, rather than an inlineobject. - A group of fields used in many places: a named
objecttype. - Anything for URLs:
slug, notstring, so editors get the generator and you get consistent formatting. - Flags used in filters:
booleanwith aninitialValue.
FAQ: Sanity Schema Types
Both store plain strings. A string field shows a single-line input and suits titles and labels, while text shows a multi-line box with a configurable number of rows for longer plain content like excerpts. Neither supports formatting; use an array of block for rich text.
Yes. Define an object or document type with a name, register it in your schema types array and use that name as the type of other fields. You can also wrap built-in types, for example a named string type with preset options, and reuse it everywhere.
The slug type stores an object with a current property so it can carry extra data and work consistently with its input. In practice, you read the value with slug.current in GROQ and in your frontend.
Use an array with string members. Set options.layout to tags for a tag-style input, or add options.list to the array to show a set of predefined checkboxes that editors can pick from.
The block type represents a paragraph or heading of rich text, including inline marks such as bold and links. You use it inside an array to create a Portable Text field, and you can add other types to the same array for embedded images or custom components.
You can change the schema at any time, but existing documents keep their stored data in the old shape. Studio will show a warning on mismatched values, so plan a migration script that converts the data when you change a field's type.
Conclusion
Sanity's built-in schema types cover almost every content need: primitives for simple values, structured types like slug and image with purpose-built inputs, references for relationships, and objects, arrays and blocks for composing rich, nested content. Knowing what each one stores in the Content Lake makes your GROQ queries and frontend code simpler.
Use this reference when you design new fields, and keep your choices consistent across types. Once you're comfortable with the building blocks, read content modelling best practices in Sanity to learn how to combine them into a model that scales.


