VA Forms Library - About schema and uiSchema
The VA.gov Forms Library lets you build web-based forms using the JSON Schema standard for form data and React for the form UI. The form data and UI are represented by schema
and ui:schema
, respectively, which are included in the form configuration file.
schema object
Properties | Value Type | Definition | Example |
---|---|---|---|
type | string | Type of schema object. Currently, the type on the top level of schema object is ‘object’. |
|
required | array | The list of required fields of the current page. It takes an array of property names passed as strings. |
|
object | See the schema object properties section. | ||
definition | object | Definition of the fields that are commonly used. The field property can use this definition by setting the reference to the definition name.
|
|
title | string | Title of field data. It can be used when there is no title defined on the uiSchema. |
|
schema object example
From the Evidence Upload 10182 Form:
schema: {
type: 'object',
required: ['evidence'],
properties: {...
},
}
schema object properties
This is an example of the object that contains the fields on each page. Each property is the field name of each field.
properties: {
myField1: {
type: myFieldType1,
},
myField2: {
type: myFieldType2,
},
}
"myField1" and "myField2" are the names of the fields.
"myFieldType1" and "myFieldType2" are the types of the field. They can be set as "number", "string", "object", "boolean" or "array".
Property Type
The property type of the field of a schema object can be one of the following:
boolean
For the property type ‘boolean’, you can customize it through the following properties: enum
, enumNames
and default
.
Properties | Value Type | Definition | Example |
---|---|---|---|
type | string | The value is set to ‘boolean.’ |
|
enum | array | This property is normally used with enumName. The value can be ‘true’ or ‘false’ (optional). |
|
enumNames | array | The name that represents the property “enum” (optional). |
|
default | boolean | The default value of the field (optional). |
|
Example of field with type ‘boolean’
From the Disability Benefits 20-0996 Form Opt-in page:
properties: {
socOptIn: {
type: 'boolean',
enum: [true, false],
enumNames: Object.values(OptInSelections),
},
},
string
For the property type ‘string’, you can customize it through the following properties: maxLength
, minLength,
enum
, enumNames
, format
, pattern
and default
.
Properties | Value Type | Definition | Example |
---|---|---|---|
type | string | The value is set to ‘string.’ |
|
maxLength | number | The maximum length of the field. |
|
minLength | number | The minimum length of the field. |
|
enum | array | This property is normally used with enumName. The value can be "true" or "false" (optional). |
|
enumNames | array | The name that represents the property "enum" (optional). |
|
default | string | The default value of the field (optional). |
|
format | format | Specify the function that will be used to format the field. |
|
pattern | string | The regular expression that is used to format the field. |
|
Examples of field with type ‘string’
From the Disability Benefits MDOT Form:
properties: {
isMilitaryBase: {
type: 'boolean',
default: false,
},
country: {
type: 'string',
},
'view:livesOnMilitaryBaseInfo': {
type: 'string',
},
street: {
type: 'string',
minLength: 1,
maxLength: 50,
pattern: '^.*\\S.*',
},
street2: {
type: 'string',
minLength: 1,
maxLength: 50,
pattern: '^.*\\S.*',
},
city: {
type: 'string',
minLength: 1,
maxLength: 51,
},
state: {
type: 'string',
},
province: {
type: 'string',
},
postalCode: {
type: 'string',
pattern: '(^\\d{5}$)|(^\\d{5}-\\d{4}$)',
},
internationalPostalCode: {
type: 'string',
},
},
From the Disability Benefits 20-0996 form Information-conference page:
informalConferenceTime: {
type: 'string',
enum: Object.keys(CONFERENCE_TIMES_V2),
enumNames: Object.values(CONFERENCE_TIMES_V2).map(name => name.label),
},
},
number
Properties | Value Type | Definition | Example |
---|---|---|---|
type | string | The value is set to ‘number’ |
|
Example of field with type ‘number’
From the Disability Benefits 21-526EZ Form:
properties: {
mostEarningsInAYear: {
type: 'number',
},
yearOfMostEarnings,
occupationDuringMostEarnings,
},
array
For the property type ‘array’, you can customize it through the following properties: maxItems
, minItems
and items
.
Properties | Value Type | Definition | Example |
---|---|---|---|
type | string | The value is set to ‘array.’ |
|
maxItems | number | The maximum items of array. |
|
minItems | number | The minimum items of array. |
|
items | object | Item object in array. |
|
Example of field with type ‘array’
From the Appeals 10182 Form:
properties: {
additionalIssues: {
type: 'array',
maxItems: 100,
minItems: 1,
items: {
type: 'object',
required: ['issue', 'decisionDate'],
properties: {
issue: {
type: 'string',
},
decisionDate: {
type: 'string',
},
},
[SELECTED]: 'boolean',
},
},
},
object
For the property type ‘object’, you can have nested object to the property properties
.
Properties | Value Type | Definition | Example |
---|---|---|---|
type | string | The value will be set to ‘object.’ |
|
required | array | The list of required fields of the current page. It takes an array of property names passed as strings. |
|
properties | object | The list of fields in this object. |
|
Example of field with type ‘object’
From the COVID-VACCINE-TRIAL Form:
properties: {
descriptionText: {
type: 'object',
properties: {
'view:descriptionText': {
type: 'object',
properties: {},
},
},
},
formCompleteTimeText: {
type: 'object',
properties: {
'view:formCompleteTimeText': {
type: 'object',
properties: {},
},
},
},
healthHeaderText: {
type: 'object',
properties: {
'view:healthText': {
type: 'object',
properties: {},
},
},
},
...
}
Using the required property, from the Federal Orders page of the 21-526EZ Form to file for disability compensation:
export const schema = {
type: 'object',
properties: {
serviceInformation: {
type: 'object',
properties: {
reservesNationalGuardService: {
type: 'object',
required: ['view:isTitle10Activated'],
properties: {
'view:isTitle10Activated': {
type: 'boolean',
},
title10Activation,
},
},
},
},
},
};
uiSchema object
The uiSchema
object was introduced by react-jsonschema-form, or RJSF, as a means of describing how a form page should be rendered from a schema
. To generate a form, react-jsonschema-form steps through the schema depth and renders different React components based on the type of data each property in the schema represents. In the Forms Library, uiSchema
follows the format described in the react-jsonschema-form documentation, with some custom Forms Library additions. The schema
and uiSchema
objects should have a similar structure, with the same fields organized in the same way, with these exceptions:
uiSchema
doesn't need to contain all the fields found in theschema
object.uiSchema
doesn't need aproperties
object for sub-fields.
For example, given this schema:
{
type: 'object',
properties: {
field1: {
type: 'string'
}
}
}
The matching uiSchema
would be:
{
'ui:title': 'My form',
field1: {
'ui:title': 'My field'
}
}
For array fields, you must specify an items
object that contains the fields for each row in the array in the uiSchema
object:
{
'ui:title': 'My form',
toursOfDuty: {
items: {
branchName: {
'ui:title': 'Branch'
}
}
}
}
Configuring uiSchema using RJSF options
If you're not already familiar with the RJSF uiSchema options, see the RJSF library documentation. Here are some commonly used options:
Properties | Value Type | Definition | Example |
---|---|---|---|
ui:order | array | An array of field names in the order in which they should appear. |
|
ui:widget | string | Deprecated. We recommend using web component patterns and fields. The name of an alternative widget to use for the field, for example, a custom widget called |
|
ui:field | string | The name of a custom field. |
|
Configuring uiSchema using Forms Library options
The Forms Library code includes additional uiSchema
functionality not found in the RJSF library.
Properties | Value Type | Definition | Example |
---|---|---|---|
ui:title | string or component | Used instead of the `title` property in the JSON Schema. It can also be a component, which passes the current form data as a property. |
or
|
ui:autocomplete | string | Set the "autocomplete" property on input - see https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete#values |
|
ui:description | string or component | Used instead of the `description` property in the JSON Schema. This can be a string or a React component, and is normally used on object fields in the schema to provide description text or HTML before a block of fields. |
or
|
ui:field | component | The component of a custom field. |
|
ui:webComponentField | component | Renders a design system web component for the field. Learn more here. |
|
ui:widget | component | Deprecated. We recommend using web component patterns and fields. The component of an alternative widget to use for the field. |
|
ui:reviewWidget | component or function | Deprecated. We recommend using web component patterns and fields. Renders string fields on the review page. Always used when you specify a custom widget component. Can also be used with regular widgets. |
or
|
ui:objectViewField | component | Renders a custom object field on the review page. This function creates the wrapper around the `ui:reviewField` content. This component is passed in parameters including all the `props` (e.g. `formContext`, `schema`, `uiSchema`, etc), `title`, `renderedProperties` (the rendered `reviewField`) and `editButton` which is used to render the review entry. Note: this entry will must be defined in the uiSchema page, outside of the defined item (one level up). |
|
ui:required | function | Provides a function to make a field conditionally required. The data in the whole form, with no page breaks, 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:validations | array | An array of validation functions or objects that you can use to add validation that's not possible through JSON Schema. |
|
ui:errorMessages | object | An object with field-specific error messages. Structured by error name (from JSON Schema error types). This is passed to custom validations in `ui:validations` in order to allow configurable error messages in a validator. |
|
object | An object that contain the configuration for each field. |
Examples of uiSchema
From the contactInfo page of 10182 Form (Request a Board Appeal):
uiSchema: {
'ui:title': ' ',
'ui:description': ContactInfoDescription,
'ui:required': () => true,
'ui:validations': [contactInfoValidation],
'ui:options': {
hideOnReview: true,
forceDivWrapper: true,
},
},
From under the field object from the areaOfDisagreement page of 10182 Form (Request a Board Appeal):
uiSchema: {
areaOfDisagreement: {
items: {
'ui:title': issueName,
'ui:description': issusDescription,
'ui:required': () => true,
'ui:validations': [areaOfDisagreementRequired],
'ui:errorMessages': {
required: missingAreaOfDisagreementErrorMessage,
},
'ui:options': {...
},
disagreementOptions: {
serviceConnection: {
'ui:title': serviceConnection,
'ui:reviewField': AreaOfDisagreementReviewField,
},
effectiveDate: {
'ui:title': effectiveDate,
'ui:reviewField': AreaOfDisagreementReviewField,
},
evaluation: {
'ui:title': evaluation,
'ui:reviewField': AreaOfDisagreementReviewField,
},
other: {
'ui:title': other,
'ui:reviewField': AreaOfDisagreementReviewField,
},
},
otherEntry: {
'ui:title': otherLabel,
'ui:description': otherDescription,
'ui:required': otherTypeSelected,
'ui:options': {...
},
'ui:errorMessages': {
required: missingAreaOfDisagreementOtherErrorMessage,
},
},
},
},
},
ui:options
ui:options
contains the configuration for each field. The configuration can be set through the following properties:
Properties | Value Type | Definition | Example |
---|---|---|---|
labels | object | A map of enum values to labels that are shown by the select and radio patterns. |
|
nestedContent | object | A map of values to a component, text, or JSX (https://reactjs.org/docs/introducing-jsx.html). If your field is a radio pattern, the content here is shown underneath the radio button for that value when it's selected. |
|
widgetClassNames | string | Deprecated. We recommend using web component patterns and fields. A string of class names that are added to the widget for the current field. widgetClassNames` is similar to the default `classNames` property, but it puts the class names on the input/select/etc element itself, rather than a surrounding `div.` |
|
viewField | component | For array fields, this component is shown when the item in the array is rendered as read-only on a page that is not a review page. |
|
doNotScroll | boolean | For array fields, this toggles the auto-scroll that happens when an item is added to the list. This setting toggles the scroll for both the original page and the review page. |
|
expandUnder | string | To show a field only when another field is true, set this option to the property name. It wraps the fields with an ExpandingGroup component using the `expandUnder` field as the first question. |
|
expandUnderCondition | string | To match to a specific value, use the `expandUnderCondition` option to specify the value that the `expandUnder` field's data should equal: expandUnderCondition: 'someValue', `expandUnderCondition` can also be a function that receives the data from the `expandUnder` field as an argument. |
|
expandUnderClassNames | string | When using the expandUnder option, you can set `expandUnderClassNames` on the field specified by `expandUnder` and it will add classes to the `div` that wraps all of the fields when they're expanded. |
|
hideOnReview | boolean or function | Hides the specified field on the review page. |
|
hideOnReviewIfFalse | boolean | Hides the specified field on the review page when the field value is `false.` |
|
hideEmptyValueInReview | boolean | Hide review row entry if the form value is an empty string, null or undefined. This option is ignored if there is a custom `'ui:reviewField'` defined. |
|
hideLabelText | boolean | Hides label added before the field. |
|
showFieldLabel | boolean | Use label or legend (inside fieldset). |
|
hideIf | function | A function that conditionally hides fields in the form. `hideIf` provides the `index` argument when you use `ui:required` on data inside an array. |
|
updateSchema | function | A function that conditionally replaces the current field's schema. `updateSchema` provides the `index` argument when you use `ui:required` on data inside an array. |
|
keepInPageOnReview | boolean | By default, array fields that are displayed on a single page in a form, such as information for multiple dependents, are displayed in a separate section on the review page. To keep the information in a single section on a review page, set this property to `true.` |
|
confirmRemove | boolean |
|
|
confirmRemoveDescription | string |
|
|
itemAriaLabel | function | Returns a string and appends it to the |
|
Examples of 'ui:options'
Using expandUnder and expandUnderCondition from [formFields.isDependent]
field on Contact Us Form 0873:
'ui:options': {
expandUnder: 'veteranStatus',
expandUnderCondition: 'dependent',
},
Using labels and nestedContent from applicantInformation
field on Apply for pre-need eligibility determination Form 40-10007:
'ui:options': {
labels: {
1: 'I am the service member/Veteran',
2: 'Spouse or surviving spouse',
3: 'Unmarried adult child',
4: 'Other',
},
nestedContent: {
1: veteranRelationshipDescription,
2: spouseRelationshipDescription,
3: childRelationshipDescription,
4: otherRelationshipDescription,
},
}
Using doNotScroll and keepInPageOnReview from the assets
field on Request help with VA debt Form 5655:
'ui:options': {
viewField: CardDetailsView,
doNotScroll: true,
itemName: 'vehicle',
keepInPageOnReview: true,
},
Using hideOnReview from the permanentAddressUI
field on the MDOT Form to order hearing aid batteries and accessories:
'ui:options': {
viewComponent: AddressViewField,
hideOnReview: formData =>
formData['view:currentAddress'] !== 'permanentAddress',
},
Using hideIf and hideOnReviewIfFalse from the isMilitaryBase
field on the MDOT Form to order hearing aid batteries and accessories:
'ui:options': {
hideIf: () => !isMilitaryBaseAddress,
hideOnReviewIfFalse: true,
useDlWrap: true,
},
Using hideIf and hideOnEmptyValueInReview from the address
field on the 686C-674 Form to add or remove a dependent on your VA disability benefits:
'ui:options': {
hideIf: () => !isMilitaryBaseAddress,
hideEmptyValueInReview: true,
},
Using showFieldLabel and expandUnder from the DIAGNOSED_DETAILS
field on the COVID-VACCINE-TRIAL Form:
'ui:options': {
showFieldLabel: true,
expandUnder: 'diagnosed',
},
Using updateSchema from the otherEntry
field on the Request a Board Appeal 10812 Form:
'ui:options': {
hideIf: (formData, index) => !otherTypeSelected(formData, index),
updateSchema: (formData, _schema, uiSchema, index) => ({
type: 'string',
maxLength: calculateOtherMaxLength(
formData.areaOfDisagreement[index],
),
}),
Putting it together
Example 1
This is an example from the Veteran information page on the 10-10CG Form, which is used to apply for comprehensive assistance for family caregivers.
The uiSchema has the following properties: ui:description
, veteranFields.fullName
, veteranFields.ssn
, veteranFields.dateOfBirth
and veteranFields.gender
.
The schema has veteranFields.fullName
, veteranFields.ssn
and veteranFields.dateOfBirth
as required fields.
From the formConfig for the 10-10CG Form:
import vetInfoPage from './chapters/veteran/vetInfo';
...
const formConfig = {
...,
chapters: {
veteranChapter: {
title: 'Veteran information',
pages: {
veteranInfoOne: {
path: 'vet-1',
title: 'Veteran information',
uiSchema: vetInfoPage.uiSchema,
schema: vetInfoPage.schema,
},
...
},
},
...
},
};
export default formConfig;
From the Veteran Information page on the 10-10CG Form:
import fullSchema from 'vets-json-schema/dist/10-10CG-schema.json';
import { VetInfo } from 'applications/caregivers/components/AdditionalInfo';
import { veteranFields } from 'applications/caregivers/definitions/constants';
import { vetInputLabel } from 'applications/caregivers/definitions/UIDefinitions/veteranUI';
import {
dateOfBirthUI,
fullNameUI,
genderUI,
ssnUI,
} from 'applications/caregivers/definitions/UIDefinitions/sharedUI';
const { veteran } = fullSchema.properties;
const veteranProps = veteran.properties;
const vetInfoPage = {
uiSchema: {
'ui:description': VetInfo({ headerInfo: true }),
[veteranFields.fullName]: fullNameUI(vetInputLabel),
[veteranFields.ssn]: ssnUI(vetInputLabel),
[veteranFields.dateOfBirth]: dateOfBirthUI(vetInputLabel),
[veteranFields.gender]: genderUI(vetInputLabel),
},
schema: {
type: 'object',
required: [
veteranFields.dateOfBirth,
veteranFields.fullName,
veteranFields.ssn,
],
properties: {
[veteranFields.fullName]: veteranProps.fullName,
[veteranFields.ssn]: veteranProps.ssnOrTin,
[veteranFields.dateOfBirth]: veteranProps.dateOfBirth,
[veteranFields.gender]: veteranProps.gender,
},
},
};
export default vetInfoPage;
Using a common front-end definition
This example is deprecated. We recommend using web component patterns.
There are a few things happening here on the veteranFields.fullName
field.
First, we've pulled the
fullName
definition fromfullSchema
. JSON Schema's method of code reuse has you put definitions in adefinitions
object in the schema.Second, we've imported
fullNameUI
from our common front-end definitions. For this field, we just have someuiSchema
configuration that sets labels and widget types. Other fields are more complex, and may require you to call a function to generate the right configuration, and possibly import something for the regularschema
object as well.Third, in the code you can see that we're using
fullNameUI
in theuiSchema
object.
The veteranFields.ssn
, veteranFields.dateOfBirth
and veteranFields.gender
fields use common frontend definitions as well by importing ssnUI
, dateOfBirthUI
and genderUI
respectively.
Example 2
This is an example from the Deceased Veteran Information page on the 21P-530 Form, used to apply for burial benefits.
The uiSchema has the following properties: deathDate
, burialDate
, view:burialDateWarning
, locationOfDeath
and ui:validations
.
The schema has deathDate
, burialDate
and locationOfDeath
as required fields.
From the Burial Benefits 21P-530 Form:
burialInformation: {
title: 'Burial information',
path: 'veteran-information/burial',
uiSchema: {
deathDate: currentOrPastDateUI('Date of death'),
burialDate: currentOrPastDateUI(
'Date of burial (includes cremation or interment)',
),
'view:burialDateWarning': {
'ui:description': burialDateWarning,
'ui:options': {
hideIf: formData => {
// If they haven’t entered a complete year, don’t jump the gun and show the warning
if (formData.burialDate && !isFullDate(formData.burialDate)) {
return true;
}
// Show the warning if the burial date was more than 2 years ago
return isEligibleNonService(formData.burialDate);
},
},
},
locationOfDeath: {
location: {
'ui:title': 'Where did the Veteran’s death occur?',
'ui:widget': 'radio',
'ui:options': {
labels: locationOfDeathLabels,
},
},
other: {
'ui:title': 'Please specify',
'ui:required': form =>
get('locationOfDeath.location', form) === 'other',
'ui:options': {
expandUnder: 'location',
expandUnderCondition: 'other',
},
},
},
'ui:validations': [validateBurialAndDeathDates],
},
schema: {
type: 'object',
required: ['burialDate', 'deathDate', 'locationOfDeath'],
properties: {
deathDate,
burialDate,
'view:burialDateWarning': { type: 'object', properties: {} },
locationOfDeath,
},
},
},
The object locationOfDeath
contains location
and other
fields.
The other
field has properties ui:title
, ui:required
and ui:options
.
The ui:options
of other
has properties expandUnder
and expandUnderCondition
. The field other
is shown under field location
when the ‘Other’ options is selected.
From the Burial Benefits 21P-530 Form:
locationOfDeath: {
location: {
'ui:title': 'Where did the Veteran’s death occur?',
'ui:widget': 'radio',
'ui:options': {
labels: locationOfDeathLabels,
},
},
other: {
'ui:title': 'Please specify',
'ui:required': form =>
get('locationOfDeath.location', form) === 'other',
'ui:options': {
expandUnder: 'location',
expandUnderCondition: 'other',
},
},
},
Conditionally displayed fields
Often when building forms you'll need to hide and show fields based on form data or other information. In this example, we use conditionally displayed fields in two places : other
(location) and view:burialDateWarning
, and we use it with two different methods:
Expand under fields: A common pattern is to expand some fields "underneath" others, when a user enters or selects information in a field that requires more information to be collected. In this example, we want to hide and show the
other
field based onlocation
(this pattern is no longer encouraged due to accessibility concerns).
Conditionally hidden fields: If you just need to hide or show a field, without the expand under treatment, you can use
hideIf
. In this example, we usehideIf
on theview:burialDateWarning
field.
Help and feedback
Get help from the Platform Support Team in Slack.
Submit a feature idea to the Platform.