Type something to search...
Sanity Schema Types: A Complete Reference

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

TypeUse it forStored as
stringShort single-line text, titles, enumsString
textMulti-line plain textString
numberPrices, quantities, ratingsNumber
booleanOn/off flagstrue or false
dateCalendar dates without time"YYYY-MM-DD" string
datetimeTimestampsISO 8601 string in UTC
urlLinksString
emailEmail addressesString
slugURL segmentsObject with current
imageImages with hotspot and cropObject with an asset reference
filePDFs, videos, downloadsObject with an asset reference
geopointMap coordinatesObject with lat, lng, alt
referenceLinks to other documentsObject with _ref
objectGroups of fieldsObject
arrayLists of values or objectsArray
blockRich text paragraphsPortable Text block
documentTop-level content typesDocument 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 rule and 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 products dataset used by several sites.
  • globalDocumentReference: points to documents in other resources, such as another dataset or the Media Library, identified by resourceType and resourceId.

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:

FieldMeaning
_idUnique ID, prefixed with drafts. for drafts
_typeThe schema type name
_createdAtWhen the document was created
_updatedAtWhen it was last changed
_revRevision 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, with options.list for fixed choices.
  • Plain paragraph: text. Formatted content: array of block.
  • Something editors might reuse across documents: a separate document type plus a reference, rather than an inline object.
  • A group of fields used in many places: a named object type.
  • Anything for URLs: slug, not string, so editors get the generator and you get consistent formatting.
  • Flags used in filters: boolean with an initialValue.

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.

Tags :
Share :

Related Posts

Building a Static Site With Astro and Sanity

Building a Static Site With Astro and Sanity

To build a static site with Astro and Sanity, keep Astro's default output: 'static', configure @sanity/astro with useCdn: false, and fetch

Dive Deeper
Common Sanity Mistakes and How to Fix Them

Common Sanity Mistakes and How to Fix Them

The most common Sanity mistakes are using the CDN where you need fresh data, fetching whole documents with ..., leaving slugs unvalidated, ship

Dive Deeper
How to Create a Custom Sanity Plugin

How to Create a Custom Sanity Plugin

To create a custom Sanity plugin, wrap a piece of Studio configuration in definePlugin from the sanity package and add it to the plugins ar

Dive Deeper