← Back to Docs
Recipe

Yup Schema Patterns

Composable validation schemas for form state, API boundaries, and runtime type safety.

Shape composition

const base = object({
  email: string().email().required(),
  age: number().min(13).max(120),
})

const extended = base.shape({
  username: string().min(3).required(),
})

Conditional validation

const schema = object({
  paymentMethod: string().oneOf(['card','crypto']),
  cardNumber: string().when('paymentMethod', {
    is: 'card',
    then: (s) => s.required().matches(/^\d{16}$/),
    otherwise: (s) => s.strip(),
  }),
})

Transform & cast

const price = number()
  .transform((v) => Math.round(v * 100) / 100)
  .typeError('Must be a number')

const tags = string()
  .transform((v) => v.split(',').map((t) => t.trim()))
  .test('max-tags', 'Max 5 tags', (arr) => arr.length <= 5)

Custom test with context

const unique = string().test(
  'unique-username',
  'Username taken',
  async (value, ctx) => {
    const exists = await checkAvailability(value)
    return !exists
  }
)

Error extraction

try {
  await schema.validate(data, { abortEarly: false })
} catch (err) {
  const fieldErrors = err.inner.reduce((acc, e) => {
    acc[e.path] = e.message
    return acc
  }, {})
}

Tip: Co-locate schemas with your form components. Export the inferred type via InferType<typeof schema> for end-to-end type safety without duplication.