Custom validation rules based on another field's value

11 replies
Last updated: May 10, 2024
Hi everyone,
I'm just wondering how can I add some custom validation rules based on another field.

So say I have X set in one field and I want to make U field required how could I achieve this with the rule.custom function from the validation property
AI Update

Great question! You can definitely use rule.custom() to create conditional validation based on another field's value. Here's how to do it:

The key is accessing the context parameter in your custom validation function, which gives you access to the parent document and other fields. Here's a practical example:

defineField({
  name: 'u',
  title: 'U Field',
  type: 'string',
  validation: (rule) =>
    rule.custom((value, context) => {
      // Check if field X has a specific value
      const xValue = context?.document?.x;
      
      if (xValue === 'someSpecificValue') {
        // Make U field required when X is set to this value
        if (!value) {
          return 'U field is required when X is set to "someSpecificValue"';
        }
      }
      
      return true; // Valid
    }),
})

Some important things to note:

Accessing other fields: Use context?.document?.fieldName to access sibling fields at the document level, or context?.parent?.fieldName for fields within the same object/array.

Return values: Your custom function should return:

  • true when the value is valid
  • An error message string when invalid
  • You can also return an object like {message: 'Error', level: 'error'} for more control

Handling undefined: Always check if values exist since fields might be empty, especially during initial document creation.

Here's a more complete example with different validation levels:

validation: (rule) =>
  rule.custom((value, context) => {
    const x = context?.document?.x;
    
    if (x === 'critical') {
      if (!value) {
        return 'U is required when X is critical'; // Error (blocks publishing)
      }
    } else if (x === 'important') {
      if (!value) {
        return { 
          message: 'Consider filling U when X is important',
          level: 'warning' // Warning (allows publishing)
        };
      }
    }
    
    return true;
  }),

You can find more details about custom validation in the official validation documentation and this specific community answer about conditional validation.

Remember that validation only runs in the Studio interface - API mutations bypass these rules, so implement server-side validation if you're accepting content through other channels!

Show original thread
11 replies
This is what i've tried so far with no luck, what is the validation rule expecting to return ?

validation: (rule) =>

rule.custom((value, context) => {

console.log("validation value", value);

console.log("context", context?.parent?.types ?? []);


if (

context?.parent?.types?.includes(SERVICE_PROVIDER_TYPE.HEALTHCARE_PROVIDER) &&

(value?.length ?? 0) === 0

) {

return rule.required();

}


return false;

}),
Regarding the return type this is what the docs say:
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, which should return either true (in the case of a valid value) or an error message as a string (in the case of an invalid value).
btw I think you can write
(value?.length ?? 0) === 0
as
!value?.length
as well
Hope that helps
Ah my bad I've completely missed that section, that makes more sense now thinking about it.
I ended up with something like this:


defineField({

name: "conditionTypes",

title: "Condition severity",

type: "array",

of: [{ type: "string" }],

validation: (rule) =>

rule.custom((value, context) => {

console.log("validation value", value);

console.log("types field value", context?.parent?.types ?? []);


if (context?.parent?.types?.includes(SERVICE_PROVIDER_TYPE.HEALTHCARE_PROVIDER) && !value?.length) {

return "Required";

}


return true;

}),

// If the "types" doesn't include the HEALTHCARE_PROVIDER value then hide this field, it's only applicable for service providers who offer HEALTHCARE_PROVIDER

hidden: ({ parent, value }) => !parent?.types?.includes(SERVICE_PROVIDER_TYPE.HEALTHCARE_PROVIDER) ?? true,

options: {

list: CONDITION_SEVERITY_OPTIONS?.map((conditionSeverity) => ({

title: conditionSeverity.label,

value: conditionSeverity.value

}))

}

})
I guess if using rule.custom() you have to do all of the sanity checks yourself so you can't chain then together which probably makes sense in a way.
No you can chain them but not inside the custom function as far as I am aware.
Example from my own codebase:
validation: (rule: Rule) => [
    rule.required(),
    rule.custom(validateSlug),
    rule.custom(maxLengthSlug(60)).warning(),
]
Ah cool that seems cool, what if one of those checks is condition if x is set make y required ? I take it you would just need to switch our required for a custom like you did for validating a slug.
Yes I think for that kind of logic you would need to go full custom and write your own implementation, for this example required goes first as the other validations don’t have to run with 0 input.
Coolio. Appreciate the responses 😄 All makes sense now 👍

Sanity – Build the way you think, not the way your CMS thinks

Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.

Was this answer helpful?