Skip to main content
Validation utilities help you validate documents, work with validation rules, and handle validation errors in Sanity Studio.

validateDocument

Validate a document against its schema and custom rules.
import {validateDocument} from 'sanity'

const result = await validateDocument({
  document,
  workspace,
  getClient,
})
document
SanityDocument
required
The document to validate
workspace
Workspace
required
Current workspace configuration
getClient
(config: SourceClientOptions) => SanityClient
required
Function to get a configured Sanity client
schema
Schema
Schema to validate against (defaults to workspace schema)
maxCustomValidationConcurrency
number
Maximum concurrent custom validation functions. Default: 5
maxFetchConcurrency
number
Maximum concurrent fetch requests during validation. Default: 25
markers
ValidationMarker[]
Array of validation markers
level
'error' | 'warning' | 'info'
Severity level
path
Path
Field path where the issue occurs
message
string
Validation message
item
ValidationMarker
Nested validation details

Example

import {validateDocument, useWorkspace, useSource} from 'sanity'
import {useState} from 'react'
import {Button, Card, Stack, Text} from '@sanity/ui'

function DocumentValidator({document}: {document: SanityDocument}) {
  const workspace = useWorkspace()
  const source = useSource()
  const [markers, setMarkers] = useState([])
  const [validating, setValidating] = useState(false)
  
  const validate = async () => {
    setValidating(true)
    
    const result = await validateDocument({
      document,
      workspace,
      getClient: source.getClient,
    })
    
    setMarkers(result)
    setValidating(false)
  }
  
  return (
    <Stack space={3}>
      <Button onClick={validate} loading={validating}>
        Validate Document
      </Button>
      
      {markers.length > 0 && (
        <Card padding={3} tone="critical">
          <Stack space={2}>
            {markers.map((marker, i) => (
              <Text key={i} size={1}>
                {marker.level}: {marker.message}
              </Text>
            ))}
          </Stack>
        </Card>
      )}
    </Stack>
  )
}

validateDocumentWithReferences

Validate a document including reference validation.
import {validateDocumentWithReferences} from 'sanity'

const {validation, referencedDocuments} = await validateDocumentWithReferences({
  document,
  workspace,
  getClient,
})
document
SanityDocument
required
Document to validate
workspace
Workspace
required
Workspace configuration
getClient
function
required
Client getter function
validation
ValidationStatus
Validation result
isValidating
boolean
Whether validation is in progress
markers
ValidationMarker[]
Validation markers
hasError
boolean
Whether there are error-level markers
hasWarning
boolean
Whether there are warning-level markers
referencedDocuments
SanityDocument[]
Documents referenced by the validated document

useValidationStatus

Hook to observe document validation status:
import {useValidationStatus} from 'sanity'

function ValidationIndicator({documentId, documentType}: Props) {
  const {isValidating, markers} = useValidationStatus(documentId, documentType)
  
  if (isValidating) return <Spinner />
  
  const errors = markers.filter(m => m.level === 'error')
  
  return (
    <Badge tone={errors.length > 0 ? 'critical' : 'positive'}>
      {errors.length > 0 ? `${errors.length} errors` : 'Valid'}
    </Badge>
  )
}
documentId
string
required
Document ID
documentType
string
required
Document type
status
ValidationStatus
Current validation status
isValidating
boolean
Whether validation is in progress
markers
ValidationMarker[]
Validation markers
hasError
boolean
Whether there are errors
hasWarning
boolean
Whether there are warnings

Rule

Validation rule builder:
import {Rule} from 'sanity'

export default {
  name: 'article',
  type: 'document',
  fields: [
    {
      name: 'title',
      type: 'string',
      validation: (Rule: Rule) => Rule.required().min(10).max(100),
    },
    {
      name: 'email',
      type: 'string',
      validation: (Rule: Rule) => Rule.email(),
    },
  ],
}

Validation Methods

required
function
Mark field as required
Rule.required()
min
function
Minimum value or length
Rule.min(10)
max
function
Maximum value or length
Rule.max(100)
length
function
Exact length (for arrays and strings)
Rule.length(5)
email
function
Validate as email
Rule.email()
regex
function
Validate against regex pattern
Rule.regex(/^[A-Z]/, {name: 'uppercase', invert: false})
uri
function
Validate as URI
Rule.uri({scheme: ['http', 'https']})
custom
function
Custom validation function
Rule.custom((value) => {
  if (value && value.includes('badword')) {
    return 'Cannot contain bad words'
  }
  return true
})
error
function
Set custom error message
Rule.required().error('Title is required')
warning
function
Set as warning instead of error
Rule.min(10).warning('Title should be at least 10 characters')

inferFromSchema

Infer validation rules from schema:
import {inferFromSchema} from 'sanity'

const rules = inferFromSchema(schema, schemaType)
schema
Schema
required
Studio schema
schemaType
SchemaType
required
Type to infer rules for
rules
Rule[]
Inferred validation rules

ValidationContext

Context available to custom validation functions:
import {type ValidationContext} from 'sanity'

Rule.custom(async (value, context: ValidationContext) => {
  const {document, getClient, parent, path} = context
  
  const client = getClient({apiVersion: '2024-01-01'})
  const exists = await client.fetch('count(*[slug.current == $slug]) > 0', {
    slug: value,
  })
  
  if (exists) return 'Slug must be unique'
  return true
})
context
ValidationContext
Validation context
document
SanityDocument
The document being validated
parent
any
Parent value in the document tree
path
Path
Current field path
getClient
function
Function to get a Sanity client
currentUser
CurrentUser
Current user information

Example: Custom Validation

import {type Rule} from 'sanity'

export default {
  name: 'article',
  type: 'document',
  fields: [
    {
      name: 'slug',
      type: 'slug',
      validation: (Rule: Rule) => [
        Rule.required(),
        Rule.custom(async (slug, context) => {
          if (!slug?.current) return true
          
          const {document, getClient} = context
          const client = getClient({apiVersion: '2024-01-01'})
          
          const exists = await client.fetch(
            'count(*[_type == "article" && slug.current == $slug && _id != $id]) > 0',
            {slug: slug.current, id: document._id}
          )
          
          return exists ? 'Slug must be unique' : true
        }),
      ],
    },
    {
      name: 'publishDate',
      type: 'datetime',
      validation: (Rule: Rule) =>
        Rule.min(new Date().toISOString())
          .error('Publish date cannot be in the past'),
    },
    {
      name: 'tags',
      type: 'array',
      of: [{type: 'string'}],
      validation: (Rule: Rule) =>
        Rule.min(1).max(10).unique(),
    },
  ],
}

Build docs developers (and LLMs) love