Skip to main content
Skip table of contents

VA Forms Library - How to create custom fields and widgets

How does the Forms Library use RJSF?

The Forms Library code uses react-jsonschema-form, or RJSF, to render form fields, but it builds a scaffolding on top of it to support multi-page forms and common form patterns. Additionally, the Forms Library uses RJSF to create a form configuration spec that allows developers to specify the structure of a multi-page form.

Customizing fields, widgets, and events from RJSF

RJSF passes all field and widget components to SchemaField(and most other components) as a registry property. To override fields and widgets in the registry, pass components of the same name into the library's main Form component. The Forms Library uses custom versions of these components:

The Forms Library uses these custom fields and widgets:

Let's understand the implementation of a custom widget with this PhoneNumberWidget example from the PhoneNumberWidget.jsx file:

JSX
<TextWidget
  {...this.props}
  type="tel"
  autocomplete="tel"
  value={this.state.val}
  onChange={this.handleChange}
/>

PhoneNumberWidget is a custom widget which allows only numbers in an input field. We’re using the TextWidget component and customizing the widget by adding an onChange event which triggers the handleChange method to restrict non-numeric values.

Now, how should we use this custom widget in a form? Let’s look at the following example in the uiSchema:

JSON
phone: {
  'ui:title': 'Phone',
  'ui:widget': PhoneNumberWidget,
  'ui:errorMessages': {
    required: 'Please enter your phone number',
    pattern: 'Please enter a valid phone number',
  },
}

To use the custom component we created, we define uiSchema and pass the component PhoneNumberWidget to 'ui:widget'.

You can learn more about custom widgets and fields in the RJSF documentation. NOTE: The Forms Library doesn’t use the latest version of the RJSF library, so some of the features or options in this documentation may be unavailable when working with the Forms Library.

Custom widget interface

Writing custom widgets is similar to writing React components: A value is passed in, and an onChangehook is provided for changing data. Other properties like the schemas and field ID are also provided.

These are the properties passed to all custom widget components:

Properties

Description

disabled

Boolean to indicate if the field has been disabled through the schema.

formContext

The form context object from RJSF.

id

The string id for the particular field being used.

label

The label text for the field, typically the title from the schema.

onChange

Function that will save data entered in the widget into your form data.

onBlur

Function that marks the field as "blurred," which will reveal any validation errors for that field.

options

The ui:optionsobject from uiSchema, if present.

options.enumOptions

An array of enum options and their names, if present in the schema, inserted into options.

readonly

If the field is marked readonlyin the schema.

registry

The registry of field and widgets, generally used by fields to choose the right component to use.

required

If the field is required or not via the schema.

value

The form data for the current property.

Custom Field interface

Widgets are typically meant for input type components where data is saved to a single property in your form data. The properties they receive are derived the broader properties of field components.

The main differences are the inclusion of idSchemaand uiSchema, which along with schemaallow full access to configuration info for a particular property in your form config.

These are the properties passed to field components:

Properties

Description

disabled

Boolean to indicate if the field has been disabled through the schema.

formContext

The form context object from RJSF.

formData

The form data for the current property.

idSchema

An object with IDs for the current property and any sub properties. The $idproperty has the ID for the current property.

name

The name of the current property, from uiSchema.

onChange

Function that will save data entered in the widget into your form data.

onBlur

Function that marks the field as "blurred," which will reveal any validation errors for that field.

readonly

If the field is marked readonlyin the schema.

registry

The registry of field and widgets, generally used by fields to choose the right component to use.

required

If the field is required or not via the schema.

uiSchema

The uiSchemaobject for the current property.

Events

In addition to customizing fields and widgets, the Forms Library code hooks into a number of events provided by Formto support the form patterns found in the FormPagecomponent. These events are:

  • validate: This event is called when validation occurs. We call our custom validation, which reads uiSchema for custom validation hooks that have been included for form fields outside of what JSON Schema provides.

  • transformErrors: This event is provided when the Forms Library receives the list of JSON Schema validation errors and can return a transformed list. It replaces the messages with a set of default messages, as well as any messages provided for specific fields in uiSchema. It also moves the errors for required fields from the object level to the field level. Because JSON Schema specifies required fields with a required array on an object field schema, any errors about missing data are associated with that object and moved so they're associated with the missing field and rendered with that field on the form.

  • onError: This event is called if a user tries to submit a form with a validation error. The Forms Library sets a submittedflag in formContext, which is an object passed to all fields and components in the RJSF form. The FieldTemplatecomponent uses formContextto display all error messages to the user.

  • onSubmit: This event is called when a user submits a form with no validation errors. When this happens, the Forms Library code looks for the next page in the multi-page form and navigates to it.

  • onChange: This event is called when a user changes data in the form. The Forms Library fires a Redux action and updates the store with the new data. The reducer code does several recalculations:

    • Required fields for the schema: You can specify functions in uiSchema that set fields as optional or required based on form data. This runs them and updates the schema.

    • Remove data from hidden fields: In uiSchema, you can specify fields that are conditionally hidden based on user data. To avoid validation errors from data a user can't see, the Forms Library updates the schema to add a ui:hiddenproperty and remove any user data for those fields.

    • General schema updates: Because you can make arbitrary changes to the schema based on form data, the Forms Library must also make those changes; for example, removing options in an enumarray when a user has entered certain data.


JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.