Validation

Describes how to validate documents within the content studio

The content studio allows you to specify validation rules on your document types and fields. Field-level validation is the most specific and gives the content studio a better chance to help the user understand where the validation failed and why, whereas the document-level validation provides slightly more control since it can validate based on the values of the entire document.

Protip

Validation is currently only done client side (within the content studio). In the future, non-custom rules will be synchronized and run on the server as well. This is why we recommend you use the built-in validation methods as much as possible, resorting to custom validation only where absolutely necessary.

Basics

Validation is defined by setting the validation property on a document type or field. It takes a function which receives a Rule as the first argument. By calling methods on this rule, you add new validation modifiers. Here's an example which validates that a string field has a value and that the string is between 10 and 80 characters long:

{
  title: 'Title',
  name: 'title',
  type: 'string',
  validation: Rule => Rule.required().min(10).max(80)
}

Without the required() call, the title is also considered valid if it does not have a value.

Error levels and error messages

By default, values that do not pass the validation rules are considered errors - these will block the draft from being published until they have been resolved. You can also set a rule to be a warning, simply by calling warning() on the rule. Similarily, you can customize the error message displayed by passing a string to the warning() or error() method:

{
  title: 'Title',
  name: 'title',
  type: 'string',
  validation: Rule => Rule.max(50).warning('Shorter titles are usually better')
}

If you want to combine both warnings and errors in the same validation set, you can use an array:

{
  title: 'Title',
  name: 'title',
  type: 'string',
  validation: Rule => [
    Rule.required().min(10).error('A title of min. 10 characters is required'),
    Rule.max(50).warning('Shorter titles are usually better')
  ]
}

Referencing other fields

Sometimes you may want to build a rule that is based on the value of a different field. By calling the Rule.valueOfField method, you can achieve this.

{
  title: 'Start date',
  name: 'startDate',
  type: 'datetime',
  validation: Rule => Rule.required().min('2018-03-01T15:00:00.000Z')
},
{
  title: 'End date',
  name: 'endDate',
  type: 'datetime',
  validation: Rule => Rule.required().min(Rule.valueOfField('startDate'))
}

Note however that it only allows referencing sibling fields. If you need to refer to things outside of this scope, you will have to use document-level validation.

Custom validation

Sometimes you will need to validate values beyond what Sanity provides. The custom() method allows you to do this. It takes a function as the first argument, and should return either true (in case of a valid value) or an error message as a string in the case of an invalid value. You may also return a promise which resolves with the same return values should you need to do asyncronous operations:

{
  name: 'location',
  type: 'geopoint',
  title: 'Location of bar',
  description: 'Required, must be in Norway',
  validation: Rule =>
    Rule.required().custom(geoPoint =>
      someGeoService
        .isWithinBounds(
          {
            latitude: geoPoint.lat,
            longitude: geoPoint.lng
          },
          someGeoService.BOUNDS_NORWAY
        )
        .then(isWithinBounds => (isWithinBounds ? true : 'Location must be in Norway, somewhere'))
    )
}

Please note that custom validators are also run on undefined values, unless the rule is explicitly set as optional by calling Rule.optional(). This allows for conditionally allowing undefined values based on some external factor, with the slight drawback that you need to make sure your functions check for undefined values. Here's an example:

{
  name: 'breweryName',
  type: 'string',
  title: 'Brewery name',
  validation: Rule => Rule.custom(name => {
    if (typeof name === 'undefined') {
      return true // Allow undefined values
    }
    
    // This would crash if we didn't check
    // for undefined values first
    return name.startsWith('Brew')
      ? 'Please be more creative'
      : true
  })
}

Should you need to reference other fields from within the custom validator function, you can use the second argument (context) to the function:

{
  name: 'durationInMinutes',
  type: 'number',
  title: 'Duration of talk, in minutes',
  validation: Rule => Rule.custom((duration, context) => {
    const isShortTalk = duration && duration <= 10
    if (isShortTalk && context.document.talkType !== 'lightning') {
      return 'Only lightning talks should be 10 minutes or less'
    }
    
    return true
  })
}

You can also access the closest parent from the context, along with the path of the current element being validated.

Disabling validation

Sometimes you can set validation: false on a document or a field to disable it.

Gotcha

Validation is run whenever changes are made to the document. We will provide a way to only validate on publish in the future. Keep this in mind when doing asyncronous operations against external APIs and similar.