Skip to main content

Pivotal UI

v19.0.0

Forms

Forms handle state, validation, and error handling so that the internal components don't have to worry about them.

The Form component works by building up its fields, where the field name, initial value, label, and rendered children are defined. These fields can then be used within the Form

A Form will generally look like this:

<Form {...{
  fields: {
    fieldName: {/*...*/}
  }
}}>
{({fields}) => {
  return (
    <div>
      {fields.fieldName}
    </div>
  );
}}
</Form>

Basic Forms

We can set an initialValue to pre-fill each field. When the form is reset before submitting, all fields will revert to their initialValue, if provided. In the Email Form below, the email field has been pre-filled to my@me.com.

Email Form

We use the Form's canSubmit function to control whether or not the "Subscribe" button is disabled. We attach the Form's reset function to the "Reset" button to allow it to reset the form's state.

ReferenceError: React is not defined

Optional Fields

When you set optional: true on a field, it can affect both the appearance of the field and the behavior of the form. The text "(Optional)" is added to the field label, and the field is no longer considered required.

To change the text that is added to the label, set the optionalText property within the field object. In the example below, we have set optional: true and optionalText: '(Opt)' for the Last Name field.

Inline Forms & Label Position

When you set the inline property to true, the label gets positioned next to the field instead of above it. By default, the label will appear to the left of the field, but you can set labelPosition: 'after' to place the label on the right.

Inline Form
ReferenceError: React is not defined

Tooltips

The tooltip prop makes an icon with a tooltip appear next to the label. tooltipSize can be set to: sm, md or lg in order to control it's size. And it's placement can be controlled via the tooltipPlacement prop with the following options: left, right, bottom, top.

Tooltips
ReferenceError: React is not defined

Accessing the Form state

The Form's childen has access to its state to determine how it wants to render.

For example, one field can determine whether to hide or show another field.

Hiding or showing another field
ReferenceError: React is not defined

Or use one field to determine the contents of another field.

Dynamic Field Generation
ReferenceError: React is not defined

Client-side Validation

In this next example, we do two kinds of client-side validation.

First, we define a validator prop on the first field to make sure that the password's length is 8 or more characters. validator functions take in the current value of the field that they are validating and return either an error message (if there is a validation error) or a falsy value (if there is no error).

Next, to construct the "Save Password" button, we look at the current form state and render the button as disabled when state.current.password1 and state.current.password2 do not match.

The field is validated as the user enters a value. When the value is invalid, the canSubmit function will return false. The value will only be shown to be invalid after that field loses focus. As soon as the the value becomes valid again, canSubmit will return true, and the value will be shown to be valid again.

Due to the above behavior, we recommend against using a validator on the final field of a Form. The experience can be jarring when a user wants to click the Submit button, but is unable due to a validation error that will only be shown after the field loses focus.

Validated Fields Form
ReferenceError: React is not defined

Field ids & label fors

All fields require an id. If you do not provide one, a unique id will be generated for you. It is used to set the for attribute on the corresponding <label> tag, so that the label is semantically connected to a specific input.

<Form {...{
  fields: {
    host: {label: 'Host'},
    path: {label: 'Path', children: <Input id="the-path"/>}
  }
}}>
{({fields}) => (
  <div>
    {fields.host}
    {fields.path}
  </div>
)}
</Form>

Composite fields

When the state of a field is best represented by a collection (e.g. an array or an object), use a composite field.

An initial value must be provided in order for the Reset button to work properly.

onChange

An onChange callback needs to be provided for each input element. This callback should use the Form's onChange function to update its value. In the example below, both inputs have their own onChange callback.

Use stopPropagation within onChange to stop the Form from overriding its composite value.

Input with custom onChange
ReferenceError: React is not defined

Form submission

Define an onSubmit prop on a Form to do something with the state values on submission. The onSubmit method is passed {state: {initial, current}}.

The canSubmit function is available to help determine whether a form is ready for submission. It returns true if all required fields are filled and if all fields are different from their initial value.

By default, a button within the Form that has type="submit" will trigger submission. This behavior can also be attached to another field that takes in the onSubmit, as shown below.

Form submission
ReferenceError: React is not defined

Form error handling

Define a onSubmitError handler to map error messages to a specific field. Return an object keyed by the field's name to determine where the error is shown.

Form submission

The error is attached to the first name field.

ReferenceError: React is not defined

Using the FormUnit

To lay out a single form field without using a whole Form component, you can use the FormUnit component. The FormUnit component can decorate a field with a label, a tooltip, an "optional" indicator, and help text.

Note that state management and other Form features are not handled by the FormUnit.

Examples

Basic form unit
ReferenceError: React is not defined
Inline form unit

When inline is true, the label will be placed on the same line as the field.

ReferenceError: React is not defined
Form unit with error

When hasError is true, the field border and help text become red to indicate an error.

ReferenceError: React is not defined
Form unit with postLabel

The postLabel can contain any node and will be positioned in the top-right corner of a non-inline form unit.

ReferenceError: React is not defined
Form unit with composite field
ReferenceError: React is not defined

Props

Form props

PropertyRequiredTypeDefaultDescription
afterSubmitnofunction() => {}Called with {state, setState, response, reset}. response is the return value of onSubmit.
childrennonode or functionCalled with {fields, canSubmit, canReset, reset, onSubmit, setState, state, onChange, onBlur, onChangeCheckbox}
fieldsnoobjectA collection of the inputs to track.
onModifiednofunction() => {}Called on every state change. Called with true when current state is different from initial state. false when they are the same.
onSubmitnofunction() => {}Called with the state, {initial, current}. If this function is async, we will await it.
onSubmitErrornofunction() => {}Called with any error that onSubmit throws. Should return object mapping from field name -> String.
resetOnSubmitnoboolIf true, resets the form to its initial state after onSubmit succeeds.

FormUnit props

PropertyRequiredTypeDefaultDescription
childrennonodeInput field to decorate with label
classNamenostringClass name to attach to top-level div
fieldRowClassNamenostringClass name to attach to the inner div surrounding the field
hasErrornobooleanIf true, applies error CSS. Turns border and help text red.
helpnonodeHelp block shown underneath the field
hideHelpRownobooleanfalseHides the help/error block underneath the field, so it does not take up any space
inlinenobooleanfalseIf true positions the label on the same line as the field.
labelnostringText to use for field label
labelClassNamenostringClass name to attach to the inner label element
labelFornostringValue of the label's for attribute. If not provided, defaults to the field's id.
labelPositionnooneOf(['after'])If after and inline=true positions the label after the field.
labelRowClassNamenostringClass name to attach to the inner div surrounding the label
optionalnobooleanfalseIf true, marks a field as optional and adds optionalText to label
optionalTextnostring'(Optional)'Text to add to label when field is optional
postLabelnooneOf(node, function)Content to place in the top right of a non-inline FormUnit, or a callback, called with {state, setValues}, returning that content
retainLabelHeightnobooleanfalseFor fields without a label, add an empty space above the field to preserve the space where the label would be.
setValuesnofunctionFunction passed to postLabel callback
statenoobjectObject passed to postLabel callback
tooltipnonodeContent to place on the tooltip
tooltipPlacementnooneOf('top', 'bottom', 'left', 'right')'top'Placement of tooltip in relation to icon
tooltipSizenooneOf(['sm', 'md', 'lg'])'md'Size of tooltip

Imports

Import React components (including CSS):

import {Form, FormUnit} from 'pivotal-ui/react/forms';

Import CSS only:

import 'pivotal-ui/css/forms';