Skip to content


TextField wraps a text input with label, helper, and validation text. It is intended for text input types, such as text, number, email, password, tel, etc.
Figma logo
  • We suggest using Grid to lay out your forms. We currently suggest a rowGap of 3 spacing units. A suggested form width is still being established, but 632 is a good suggestion for the time being.
  • We suggest building forms with Formik to leverage its built-in support for state management and client side validation via Yup.
  • In the UI, we do not mark required fields; rather, we mark those that are optional.
    • Add the required attribute on a field to require valid input
    • You can customize the default validation text using a validationSchema via Yup.
    • Provide a string to the optionalText prop to mark optional fields.

A basic TextField implementation using Formik and Yup to validate a required email text input.

A basic TextField implementation includes an input with a required label. Descriptive helperText can be added.

Disabled states should be used with caution.

Content can be prepended or appended

The CharacterCount component can be added to fields.

Any valid HTML attribute for the <input/> tag can be passed through TextField. In this example, we leverage various intrinsic HTML attributes to alter the behavior of the <input/>.

Setting the appropriate type on the input provides the browser with a hint as to which virtual keyboard would be most helpful when entering data in this field (relevant in mobile devices).The size and placeholder, min, and max are commonly used to set parameters for the data the field can accept.

We recommend against using input masking. Instead, we suggest using client side validation that accepts almost any format and applies a mask-like format on blur.

Optional fields should be called out in the UI.

It may sometimes be necessary to hide labels, but label text must still be made available for screen readers. This example uses visuallyHidden to visually hide the label.

React props
after  ReactNodeContent shown after the inpinputut value. By default this content is not interactive and will ignore pointer events. Any interactive elements must be styled with pointer-events: auto;
before  ReactNodeContent shown before the input value. By default this content is not interactive and will ignore pointer events. Any interactive elements must be styled with pointer-events: auto;
children  ReactNodeThe content of the component. This content will be rendered as a sibling of the native input element.
component  ReactNodeComponent override for the input element.
counter  ReactNodeIndicator of a limit placed on a field, e.g. maximum character count or remaining hours.
fit  contentcontainerDetermines the point of reference for the width of the component. If set to content, the size will be set by the size of the input. If set to container, the size will expand to fit the containing element.
helperText  ReactNodeA brief description or hint for the input; can be used to link to an external resource.
id  stringThe ID of the input element.
invalid  booleanIf true, the field will be shown in an invalid state.
label  RequiredstringLabel associated with the input.
name  RequiredstringName of the input. Submitted with the form as part of a name/value pair.
optionalText  stringText used to indicate that a field is not required.
validationText  ReactNodeValidation message associated with the input.

Gusto’s forms were designed to provide a high-quality, easy-to-use experience for our users whether or not they use assistive technologies. Colors, states, and layout were specifically chosen to provide maximum usability for all users across device types.

  • Always provide a label for each input. It can be visually hidden for UI where labels are superfluous to sighted users.
  • Avoid changing tabindex as this will affect the ability for keyboard users to navigate sequentially through the form.

The following testing snippet(s) offer suggestions for testing the component using React Testing Library with occasional help from Jest.

render(<TextField label="Label" helperText="Helper text" />);
const input = screen.getByLabelText('Label');
expect(input).toHaveAccessibleDescription('Helper text');
userEvent.type(input, 'Hello');
render(<TextField label="Label" invalid validationText="Validation text" />);
const input = screen.getByLabelText('Label');
expect(input).toBeInvalid(); // or expect(input).not.toBeValid();
expect(input).toHaveAccessibleDescription('Validation text');
helperText="Helper text"
validationText="Validation text"
const input = screen.getByLabelText('Label');
expect(input).toHaveAccessibleDescription(expect.stringContaining('Helper text'));
expect(input).toHaveAccessibleDescription(expect.stringContaining('Validation text'));
// or all at once:
expect(input).toHaveAccessibleDescription('(optional) Helper text Validation text');
render(<TextField label="Label" before="Before" after="After" />);
const inputContainer = screen.getByLabelText('Label').parentElement as HTMLDivElement;