# Conditional Yup Validation React: How to Require Fields Based on Other Input Values

> Learn conditional Yup validation in React. Dynamically require form fields based on other input values using Yup's when method and React hooks. Enhance your forms today.

- Repository: [Meta/react](https://github.com/facebook/react)
- Tags: how-to-guide
- Published: 2026-02-16

---

**You can implement conditional Yup validation in React by using Yup's `.when()` method inside your schema, coupled with React's `useState` and `useEffect` hooks (as implemented in [`packages/react/src/ReactHooks.js`](https://github.com/facebook/react/blob/main/packages/react/src/ReactHooks.js)) to dynamically recompute the validation resolver whenever a trigger field changes.**

The facebook/react repository provides the foundational hooks architecture that makes dynamic form validation possible. By leveraging React's state management alongside Yup's schema composition, you can build forms where field requirements change based on user input. This guide demonstrates how to implement conditional Yup validation react patterns using the actual source code structure from the React monorepo.

## Understanding the React Hooks Architecture for Dynamic Validation

React’s core architecture relies on a minimal, predictable hooks API defined in [`packages/react/src/ReactHooks.js`](https://github.com/facebook/react/blob/main/packages/react/src/ReactHooks.js). Two specific hooks power the conditional validation pattern:

- **`useState`** (lines 66-71): Stores the current value of the trigger field. When this state updates, React schedules a re-render via the reconciler.
- **`useEffect`** (lines 87-101): Reacts to state changes and updates the validation resolver, ensuring the form library receives the latest schema after each trigger change.

The React reconciler ([`packages/react/src/ReactFiberWorkLoop.js`](https://github.com/facebook/react/blob/main/packages/react/src/ReactFiberWorkLoop.js)) guarantees minimal re-renders by diffing the component tree, so only components subscribing to the changed state (like your form resolver) update when the conditional validation rules shift.

## Building a Conditional Yup Schema

Yup supports conditional logic via the `.when()` method, which receives the value of a dependent field and returns a tailored rule set. This creates a pure data object that React can pass to your form library.

```typescript
import * as Yup from 'yup';

const getValidationSchema = (triggerValue?: string) => {
  return Yup.object().shape({
    // The field that controls the requirement
    trigger: Yup.string().required('Please select an option'),

    // Conditionally required field
    dependent: Yup.string().when('trigger', {
      is: (val: string) => val === 'require',
      then: (schema) => schema.required('This field is required when trigger is "require"'),
      otherwise: (schema) => schema.notRequired(),
    }),
  });
};

```

## Implementing Conditional Yup Validation React with react-hook-form

The most performant approach uses `useMemo` to regenerate the Yup resolver only when the trigger field changes. This pattern aligns with React’s internal dispatcher logic found in [`packages/react/src/ReactSharedInternals.js`](https://github.com/facebook/react/blob/main/packages/react/src/ReactSharedInternals.js).

```tsx
import React, { useMemo } from 'react';
import { useForm, Controller } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import * as Yup from 'yup';

export default function ConditionalForm() {
  // Watch the controlling field to trigger schema re-evaluation
  const { control, handleSubmit, watch, formState: { errors } } = useForm({
    mode: 'onBlur',
  });

  const watchTrigger = watch('trigger');

  // Memoize the resolver so it updates only when watchTrigger changes
  // This mirrors the optimization strategies in ReactFiberWorkLoop.js
  const resolver = useMemo(() => {
    const schema = Yup.object().shape({
      trigger: Yup.string().required(),
      dependent: Yup.string().when('trigger', {
        is: (val: string) => val === 'require',
        then: (schema) => schema.required('Dependent field is required'),
        otherwise: (schema) => schema.notRequired(),
      }),
    });
    return yupResolver(schema);
  }, [watchTrigger]);

  // Update the form resolver when it changes
  const { handleSubmit: handleSubmitWithResolver } = useForm({
    resolver,
    mode: 'onBlur',
  });

  const onSubmit = (data: any) => console.log('Submitted:', data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <Controller
        name="trigger"
        control={control}
        defaultValue=""
        render={({ field }) => (
          <select {...field}>
            <option value="">Select…</option>
            <option value="require">Require Dependent</option>
            <option value="optional">Optional Dependent</option>
          </select>
        )}
      />
      {errors.trigger && <span>{errors.trigger.message}</span>}

      <Controller
        name="dependent"
        control={control}
        defaultValue=""
        render={({ field }) => <input {...field} placeholder="Dependent value" />}
      />
      {errors.dependent && <span>{errors.dependent.message}</span>}

      <button type="submit">Submit</button>
    </form>
  );
}

```

## Alternative Pattern: Using useEffect for Schema Updates

If you need explicit control over when the resolver updates, use `useEffect` to call `setResolver` when dependencies change. This pattern directly utilizes the effect hook implementation in [`packages/react/src/ReactHooks.js`](https://github.com/facebook/react/blob/main/packages/react/src/ReactHooks.js) (lines 87-101).

```tsx
import React, { useEffect, useState } from 'react';
import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import * as Yup from 'yup';

export default function EffectBasedForm() {
  const [resolver, setResolver] = useState(() => 
    yupResolver(
      Yup.object().shape({
        trigger: Yup.string().required(),
        dependent: Yup.string(),
      })
    )
  );

  const { register, handleSubmit, watch, formState: { errors } } = useForm({
    resolver,
    mode: 'onSubmit',
  });

  const trigger = watch('trigger');

  useEffect(() => {
    const schema = Yup.object().shape({
      trigger: Yup.string().required(),
      dependent: Yup.string().when('trigger', {
        is: (v: string) => v === 'require',
        then: (schema) => schema.required('Field is required'),
        otherwise: (schema) => schema.notRequired(),
      }),
    });
    setResolver(() => yupResolver(schema));
  }, [trigger]);

  const onSubmit = (data: any) => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <select {...register('trigger')}>
        <option value="">Select…</option>
        <option value="require">Require</option>
        <option value="optional">Optional</option>
      </select>
      {errors.trigger && <p>{errors.trigger.message}</p>}

      <input {...register('dependent')} placeholder="Dependent" />
      {errors.dependent && <p>{errors.dependent.message}</p>}

      <button type="submit">Submit</button>
    </form>
  );
}

```

## Key React Source Files Supporting This Pattern

Understanding the internal implementation helps optimize your conditional validation logic. These files from the facebook/react repository define the behavior you rely on:

- **[`packages/react/src/ReactHooks.js`](https://github.com/facebook/react/blob/main/packages/react/src/ReactHooks.js)** – Implements `useState` (lines 66-71) and `useEffect` (lines 87-101) that manage the trigger field state and schema updates.
- **[`packages/react/src/ReactFiberWorkLoop.js`](https://github.com/facebook/react/blob/main/packages/react/src/ReactFiberWorkLoop.js)** – Contains the reconciler’s work loop that ensures minimal re-renders when your validation resolver changes.
- **[`packages/react/src/ReactSharedInternals.js`](https://github.com/facebook/react/blob/main/packages/react/src/ReactSharedInternals.js)** – Provides the internal dispatcher that hooks resolve to, ensuring consistent behavior across renders.
- **[`packages/react-dom/src/client/ReactDOMRenderer.js`](https://github.com/facebook/react/blob/main/packages/react-dom/src/client/ReactDOMRenderer.js)** – Handles the DOM-specific rendering pipeline where your form components mount.
- **[`packages/react/src/ReactVersion.js`](https://github.com/facebook/react/blob/main/packages/react/src/ReactVersion.js)** – Defines the version string for compatibility checks with form libraries.
- **[`packages/react/src/ReactContext.js`](https://github.com/facebook/react/blob/main/packages/react/src/ReactContext.js)** – Implements context propagation useful for global form state if needed.
- **[`packages/react/src/ReactCreateElement.js`](https://github.com/facebook/react/blob/main/packages/react/src/ReactCreateElement.js)** – Underlies JSX compilation for dynamic form field generation.

## Summary

- **Use Yup's `.when()` method** to define conditional rules that reference other field values in your schema.
- **Leverage `useState` and `useEffect`** (as implemented in [`packages/react/src/ReactHooks.js`](https://github.com/facebook/react/blob/main/packages/react/src/ReactHooks.js)) to track trigger field changes and regenerate the validation resolver.
- **Memoize the resolver** with `useMemo` to prevent unnecessary re-renders, aligning with React’s reconciler optimizations in [`ReactFiberWorkLoop.js`](https://github.com/facebook/react/blob/main/ReactFiberWorkLoop.js).
- **Integrate with react-hook-form** using `yupResolver` from `@hookform/resolvers` to bind the dynamic schema to your form component.
- **Consider the `useEffect` alternative** when you need explicit control over resolver updates via `setResolver`.

## Frequently Asked Questions

### How does Yup's `.when()` method work for conditional validation?

Yup's `.when()` method allows you to create schema branches that depend on the value of sibling fields. It accepts the field name to watch, an options object with `is` (a predicate function), `then` (schema to apply when true), and `otherwise` (schema when false). This creates a pure function that React can recompute whenever form state changes.

### Why should I use `useMemo` instead of `useEffect` for the validation resolver?

Using `useMemo` to generate your `yupResolver` prevents unnecessary recalculations of the schema object on every render. Since `useMemo` only recomputes when its dependency array changes (e.g., when the trigger field value updates), it aligns with React's performance optimizations in the reconciler ([`ReactFiberWorkLoop.js`](https://github.com/facebook/react/blob/main/ReactFiberWorkLoop.js)). `useEffect` is better suited when you need to imperatively call `setResolver` or perform side effects beyond resolver creation.

### Can I implement conditional validation without react-hook-form?

Yes, you can implement conditional Yup validation with raw React state or other form libraries like Formik. The core pattern remains the same: use `useState` to track the trigger field value, and recompute the Yup schema (or validation function) whenever that value changes. You would then call `schema.validate()` manually in your submit handler or `onChange` handler, leveraging the same React hooks architecture found in [`packages/react/src/ReactHooks.js`](https://github.com/facebook/react/blob/main/packages/react/src/ReactHooks.js).

### How do I handle multiple dependent fields in a single Yup schema?

For multiple dependencies, chain `.when()` calls or use an array syntax if supported by your Yup version. You can also reference multiple fields in the `is` predicate by using Yup's context or by building a more complex schema object. Ensure your React component watches all dependent fields (e.g., `watch(['field1', 'field2'])`) and includes them in the `useMemo` dependency array so the resolver updates whenever any controlling field changes.