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 component | Form 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>