In some cases you may want to have fields be required only under certain conditions. To handle that, you can use the ui:required helper:

Properties

Value Type

Definition

Example

ui:required

function

Provides a function to make a field conditionally required. The data in the whole form is the only parameter.

Don't make a field required in the JSON schema in addition to using `ui:required` on that field.

The index argument is provided if you use `ui:required` on data inside an array.

'ui:required': formData =>formData.veteranStatus.veteranStatus === 'dependent'

or

'ui:required': (formData, _index) => formData.preferredContactMethod === 'email',

Here is a code example where myOtherField is a conditionally required field:

page1: {
  path: 'first-page',
  title: 'First Page',
  uiSchema: {
    myField: {
      'ui:title': 'My field',
    },
    myOtherField: {
      'ui:title': 'My field',
      'ui:required': (formData) => formData.myField === 'test'
    },
  },
  schema: {
    type: 'object',
    properties: {
      myField: {
        type: 'string',
      }
      myOtherField: {
        type: 'string',
      }
    }
  }
}
JS

In the above config, myOtherField will be required if myField equals "test". The formData parameter contains all of the form data, across all pages, so your conditional logic can be based on any other data in the form.

Example 1

This is an example from the Deceased Veteran information page on the 21P-530 Form, which is used to apply for burial benefits.

In this example, veteranSocialSecurityNumber and vaFileNumber are conditionally required fields.

Screen shot showing the Deceased Veteran information page on the 21P-530 Form with UI fields annotated with the corresponding schema fields

From the formConfig for the Burial Benefits 21P-530 Form:

...
const formConfig = {
  ...
  chapters: {
    claimantInformation: {
      title: 'Claimant Information',
      pages: {
    },
    veteranInformation: {
      title: 'Deceased Veteran information',
      pages: {
        veteranInformation: {
          title: 'Deceased Veteran information',
          path: 'veteran-information',
          uiSchema: {
            veteranFullName: fullNameUI,
            veteranSocialSecurityNumber: {
              ...ssnUI,
              'ui:title':
                'Social Security number (must have this or a VA file number)',
              'ui:required': form => !form.vaFileNumber,
            },
            vaFileNumber: {
              'ui:title':
                'VA file number (must have this or a Social Security number)',
              'ui:required': form => !form.veteranSocialSecurityNumber,
              'ui:options': {
                widgetClassNames: 'usa-input-medium',
              },
              'ui:errorMessages': {
                pattern: 'Your VA file number must be 8 or 9 digits',
              },
            },
            veteranDateOfBirth: currentOrPastDateUI('Date of birth'),
            placeOfBirth: {
              'ui:title': 'Place of birth (city and state or foreign country)',
            },
          },
          schema: {
            type: 'object',
            required: ['veteranFullName', 'veteranDateOfBirth'],
            properties: {
              veteranFullName,
              veteranSocialSecurityNumber,
              vaFileNumber,
              veteranDateOfBirth,
              placeOfBirth,
            },
          },
        },
      },
    },
  },
};

export default formConfig;
JS

Example 2

This is an example from the Veteran Information page on the 686C-674 Form, which is used to add or remove a dependent on VA disability benefits.

Screen shot showing the Veteran Information page on the 686C-674 Form with UI fields annotated with the corresponding schema fields

From the formConfig for the 686C-674 Form; the formCofig below uses veteranAddress to render the form as it appears in the screenshot:

import {
  ...,
  veteranAddress,
} from './chapters/veteran-information';

const formConfig = {
  ...
  chapters: {
    ...
    veteranInformation: {
      title: "Veteran's information",
      pages: {
        ...,
        veteranAddress: {
          path: 'veteran-address',
          title: 'Veteran Address',
          uiSchema: veteranAddress.uiSchema,
          schema: veteranAddress.schema,
        },
      },
    },
    ...
  },
};

export default formConfig;
JS

The uiSchema for veteranAddress imports and uses a pre-made uiSchema called addressUiSchema.  

veteranAddress: addressUISchema(
  true,
  'veteranContactInformation.veteranAddress',
  () => true,
),
JS

From addressUiSchema, stateCode, province, zipCode, and internationalPostalCode are conditionally required fields. For an international address, province and internationalPostalCode will be shown.

export const addressUISchema = (
  isMilitaryBaseAddress = false,
  path,
  callback,
) => {

  ...
  return (function returnAddressUI() {
    return {
      'view:livesOnMilitaryBase': {
        'ui:title': `${checkBoxTitleState} live on a United States military base outside of the U.S.`,
  'ui:options': {
    hideIf: () => !isMilitaryBaseAddress,
    hideEmptyValueInReview: true,
  },
      },
      'view:livesOnMilitaryBaseInfo': {
      ...
      },
      ...
      stateCode: {
        'ui:required': (formData, index) => {
          let countryNamePath = `${path}.countryName`;
          if (typeof index === 'number') {
            countryNamePath = insertArrayIndex(countryNamePath, index);
          }
          const livesOnMilitaryBase = get(livesOnMilitaryBasePath, formData);
          const countryName = get(countryNamePath, formData);
          return (
            (countryName && countryName === USA.value) || livesOnMilitaryBase
          );
        },
        'ui:title': 'State',
        'ui:errorMessages': {
          required: 'State is required',
        },
        'ui:options': {
        ...
        },
      },
      province: {
        'ui:title': 'State/Province/Region',
        'ui:options': {
          hideEmptyValueInReview: true,
          hideIf: (formData, index) => {
            let militaryBasePath = livesOnMilitaryBasePath;
            let countryNamePath = `${path}.countryName`;
            if (typeof index === 'number') {
              militaryBasePath = insertArrayIndex(
                livesOnMilitaryBasePath,
                index,
              );
              countryNamePath = insertArrayIndex(countryNamePath, index);
            }
            const livesOnMilitaryBase = get(militaryBasePath, formData);
            if (isMilitaryBaseAddress && livesOnMilitaryBase) {
              return true;
            }
            const countryName = get(countryNamePath, formData);
            return countryName === USA.value || !countryName;
          },
        },
      },
      zipCode: {
        'ui:required': (formData, index) => {
          let militaryBasePath = livesOnMilitaryBasePath;
          let countryNamePath = `${path}.countryName`;
          if (typeof index === 'number') {
            militaryBasePath = insertArrayIndex(livesOnMilitaryBasePath, index);
            countryNamePath = insertArrayIndex(countryNamePath, index);
          }
          const livesOnMilitaryBase = get(militaryBasePath, formData);
          const countryName = get(countryNamePath, formData);
          return (
            (countryName && countryName === USA.value) ||
            (isMilitaryBaseAddress && livesOnMilitaryBase)
          );
        },
        'ui:title': 'Postal Code',
        'ui:errorMessages': {
          required: 'Postal code is required',
          pattern: 'Postal code must be 5 digits',
        },
        'ui:options': {
          widgetClassNames: 'usa-input-medium',
          hideIf: (formData, index) => {
            // Because we have to update countryName manually in formData above,
            // We have to check this when a user selects a non-US country and then selects
            // the military base checkbox.
            let militaryBasePath = livesOnMilitaryBasePath;
            let countryNamePath = `${path}.countryName`;
            if (typeof index === 'number') {
              militaryBasePath = insertArrayIndex(
                livesOnMilitaryBasePath,
                index,
              );
              countryNamePath = insertArrayIndex(countryNamePath, index);
            }
            const livesOnMilitaryBase = get(militaryBasePath, formData);
            const countryName = get(countryNamePath, formData);
            if (isMilitaryBaseAddress && livesOnMilitaryBase) {
              return false;
            }
            return countryName && countryName !== USA.value;
          },
        },
      },
      internationalPostalCode: {
        'ui:required': (formData, index) => {
          let countryNamePath = `${path}.countryName`;
          if (typeof index === 'number') {
            countryNamePath = insertArrayIndex(countryNamePath, index);
          }
          const countryName = get(countryNamePath, formData);
          return countryName && countryName !== USA.value;
        },
        'ui:title': 'International postal code',
        'ui:errorMessages': {
          required: 'International postal code is required',
        },
        'ui:options': {
          widgetClassNames: 'usa-input-medium',
          hideIf: (formData, index) => {
            let militaryBasePath = livesOnMilitaryBasePath;
            let countryNamePath = `${path}.countryName`;
            if (typeof index === 'number') {
              militaryBasePath = insertArrayIndex(
                livesOnMilitaryBasePath,
                index,
              );
              countryNamePath = insertArrayIndex(countryNamePath, index);
            }
            const livesOnMilitaryBase = get(militaryBasePath, formData);
            if (isMilitaryBaseAddress && livesOnMilitaryBase) {
              return true;
            }
            const countryName = get(countryNamePath, formData);
            return countryName === USA.value || !countryName;
          },
        },
      },
    };
  })();
};
JS

The index argument is provided if you use `ui:required` on data inside an array.

In this example, data for stateCode, province, zipCode and internationalPostalCode are arrays.