When working with form data, it can make sense to keep associated data grouped together. This may be a military service history that includes multiple tours of duty, a list of dependents associated with a Veteran, or a collection of service related disabilities. Structurally, this data is represented as an array of objects in a given form’s formData. Colloquially, some application teams, engineers, and designers may refer to this particular pattern as a “list loop.” This guide will walk through the configuration required to use array based data in a schema, uiSchema, and formConfig.

List Loop

The term “list loop” originates from how the Forms Library visually presents array data. For example, if a Veteran is filling out information for a list of children, they would add the names of each child first, thereby creating a list (array data structure). Then, they would traverse through all pages for child one and enter data, before “looping back” to fill out the same data for child two.

Schema Configuration

Page One

export const someSchema = {
  type: 'object',
  properties: {
    someArrayProperty: {
      type: 'array',
      minItems: 1,
      items: {
        type: 'object',
        properties: {
          first: {},
          middle: {},
          last: {},
        },
      },
    },
  },
};
JSON

Working with array data starts with configuring a schema property that is typed as an array. In the example above, the someArrayProperty property is initialized as an array and it includes an items property. The items property represents what individual indexed entries in the array will contain. In this case, items houses biographical data.

Rendered form page for Page One Schema
Forms fields asking for child first, middle, and last name, social security number, and date of birth.
Additional forms fields asking for child first, middle, and last name, social security number, and date of birth.
Form displaying a list of editable cards for each form entry

Lists Only

Schema configuration for array data could end here and that would be okay. Visually, all array entries would be displayed on one form page and there would be no “looping” aspect to fill out data for each entry.

Page Two

export const someOtherSchema = {
  type: 'object',
  properties: {
    someArrayProperty: {
      type: 'array',
      minItems: 1,
      items: {
        type: 'object',
        properties: {
          country: ""
          state: "",
          street: "",
          city: "",
          postal: "",
        },
      },
    },
  },
};
JSON

Creating a second page for array based data to traverse through, thereby creating the list loop pattern, is practically identical to the first page. An array typed schema property is initialized with the same property name as the array property in page one. This lets react-jsonschema-form know that this data is associated. Form state and object merging is handled internal to react-jsonschema-form. In this instance, the second page of our list loop contains address data for each entry created on page one.

Rendered form page for Page Two Schema
Form fields asking for a child's place of birth and status

uiSchema Configuration

There are no specific differences to uiSchema configuration when working with array based data. However, one thing to note when working with array data and uiSchema is that ui:required provides an index argument, useful for accessing data at a particular index in the array:

{
  'ui:title': 'State',
  'ui:required': (formData, index) =>
    formData?.someArrayProperty[`${index}`]?.country ===
    "USA",
},
JS

formConfig Configuration

    someChapter: {
      pages: {
        dependentName: {
          title: 'Some title',
          path: 'some-path',
          uiSchema,
          schema,
        },
        dependentPlaceOfBirth: {
          title: 'Some title',
          path: 'some-path/:index',
          showPagePerItem: true,
          arrayPath: 'someArrayProperty',
          uiSchema,
          schema,
        },
        dependentAdditionalInformation: {
          title: 'Some title',
          path: 'some-path/:index/additional-path-name',
          showPagePerItem: true,
          arrayPath: 'someArrayProperty',
          uiSchema,
          schema,
        },
      },
    },
JS

formConfig setup is similar to regular page configuration, with several additions worth mentioning:

  • Each page that follows the first page for array based data must include a property called arrayPath whose value is equal to the property name specified in the corresponding schema. In this example, someArrayProperty.

  • showPagePerItem specifies that each item in an array will get turned into a distinct page.

  • Each additional page needs a path property that includes the :index parameter. This allows the forms library to keep track of associated data per index.

Examples in production

  1. Form 686c-674 - contains an add child workflow that includes list loops

    1. formConfig

    2. Schema configuration