Skip to main content
Kaleidoscope

Form

A set of components to handle capturing input and submitting data

The <FormComponent> uses the React Final Form library under the hood.

Default

A basic form is made up of a parent <FormComponent> and form-specific child input components, see . The <FormComponent> requires an onSubmit prop to handle the form submission action.

Editable example
<FormComponent onSubmit={(data) => window.alert(JSON.stringify(data))}>
  <div style={{display: "grid", gap: 12}}>
    <FormTextInput
      name="firstName"
      label="What is your name?"
      placeholder="Please enter your name"
    />
    <FormSegmentedControl
      name="size"
      label="How big is your sales team?"
      initialValue="0"
      options={[
        {
          label: "0",
          value: "0",
        },
        {
          label: "1-3",
          value: "1",
        },
        {
          label: "4-20",
          value: "4",
        },
      ]} />
    <FormSelect
      name="job"
      label="What is your job title?"
      placeholder="Select an option"
      initialValue=""
    >
      <SelectOption value="designer">
        UX Designer
      </SelectOption>
      <SelectOption value="engineer">
        Software engineer
      </SelectOption>
      <SelectOption value="leader">
        CEO
      </SelectOption>
    </FormSelect>

    <FormSubmitButton>
      Submit
    </FormSubmitButton>
  </div>
</FormComponent>

Form Compatible Input Elements

Regular componentForm compatible equivalent
<TextInput/><FormTextInput />
<Checkbox/><FormCheckbox />
<Select/><FormSelect />
<SegmentedControl/><FormSegmentedControl />
<Button/><FormSubmitButton />

Best practices

  • Keep the number of fields as low as possible to avoid overwhelming users. Consider splitting forms into multiple steps if the number of fields grows too long.
  • Avoid asking for unnecessary details. Where appropriate explain why details are being requested e.g. "We'll use this information to tailor your experience".
  • Consider other options to break up multi step forms, for example a grid of visual cards can be more interesting than a series of plain dropdown inputs.

Input validation

Provide a validate function to the child input components, that returns an error message when the field value is invalid. Validation runs before form submission therefore invalid data will block the <FormComponent>'s onSubmit function.

Editable example
<FormComponent onSubmit={(data) => window.alert(JSON.stringify(data))}>
  <div style={{display: "grid", gap: 12}}>
    <FormTextInput
      name="firstName"
      validate={(value) => {
        if (!value) {
          return "Name cannot be empty";
        }
      }}
      label="What is your name?"
      placeholder="Required"
    />
    <FormSubmitButton>
      Submit
    </FormSubmitButton>
  </div>
</FormComponent>

Handling form submission errors

The onSubmit function can return a FormSubmitError object when something goes wrong during form submission. Use the helper function createFormSubmitErrorObject() to create the error object format with a required general error message and optional field-specific error messages.

Showing an error message

Consume FormContext to receive the submitError, which can then be used to render error messaging to the user.

Editable example
<FormComponent onSubmit={(data) => {
    try {
      throw new Error();
    } catch (error) {
      return createFormSubmitErrorObject("Something went wrong!", {firstName: "Is that your real name?"});
    }
  }
}>
  <FormContext.Consumer>
    {({ submitError }) => (
      <div style={{display: "grid", gap: 12}}>
        {submitError && (<Alert heading="Error" alertType={AlertType.Error}>
          {submitError}
        </Alert>)}
        <FormTextInput
          name="firstName"
          label="What is your name?"
          placeholder="Please enter your name"
        />
        <FormSubmitButton>
          Submit
        </FormSubmitButton>
      </div>
    )}
  </FormContext.Consumer>
</FormComponent>

Reacting to a submit error

The <OnSubmitFailedSpy> component can trigger a function when a form submit error occurs. It must be rendered inside the <FormComponent>.

This approach can be used in addition to the FormContext approach above to show error messaging and react to an error at the same time.

Editable example
<FormComponent onSubmit={(data) => {
    try {
      throw new Error();
    } catch (error) {
      return createFormSubmitErrorObject("Some error message");
    }
  }
}>
  <div style={{display: "grid", gap: 12}}>
    <FormTextInput
      name="firstName"
      label="What is your name?"
      placeholder="Please enter your name"
    />
    <OnSubmitFailedSpy
      onSubmitFailed={(errors) => {
        if (errors) {
          window.alert("Something went wrong!");
        }
      }}
    />
    <FormSubmitButton>
      Submit
    </FormSubmitButton>
  </div>
</FormComponent>

Detecting field interaction

Use the <OnFieldValueChangeSpy> component to react to value changes. The component must be rendered inside the <FormComponent>. The onChange handler will trigger when a field is blurred AND the field's value has been changed since last blur.

Editable example
<FormComponent onSubmit={(data) => window.alert(JSON.stringify(data))}>
  <div style={{display: "grid", gap: 12}}>
    <FormTextInput
      name="firstName"
      label="What is your name?"
      placeholder="Please enter your name"
    />
    <OnFieldValueChangeSpy onChange={(field) => window.alert(`The ${field} field was changed`)} />
    <FormSubmitButton>
      Submit
    </FormSubmitButton>
  </div>
</FormComponent>

Component sizing

Providing a fieldSize prop to the <FormComponent> parent will set a consistent size on the child elements. Supported values are "small", "medium", "large".

The <FormSubmitButton> component also accepts a width prop to set the button to the width of the form.

Editable example
<FormComponent fieldSize="large" onSubmit={(data) => window.alert(JSON.stringify(data))}>
  <div style={{display: "grid", gap: 12}}>
    <FormTextInput
      name="firstName"
      label="What is your name?"
      placeholder="Please enter your name"
    />
    <FormSegmentedControl
      name="size"
      label="How big is your sales team?"
      initialValue="0"
      options={[
        {
          label: "0",
          value: "0",
        },
        {
          label: "1-3",
          value: "1",
        },
        {
          label: "4-20",
          value: "4",
        },
      ]} />
    <FormSelect
      name="job"
      label="What is your job title?"
      placeholder="Select an option"
      initialValue=""
    >
      <SelectOption value="designer">
        UX Designer
      </SelectOption>
      <SelectOption value="engineer">
        Software engineer
      </SelectOption>
      <SelectOption value="leader">
        CEO
      </SelectOption>
    </FormSelect>

    <FormSubmitButton width="full">
      Submit
    </FormSubmitButton>
  </div>
</FormComponent>