React Form Guidelines

This Form Guidelines document was created to help React developers build gorgeous and functional forms. The knowledge we are sharing here comes from years of experience with building forms, interacting with other developers building forms, and keeping up with industry best practices.

ome concepts that will be covered include how to structure horizontal or vertical form layouts, handling form validation, automatically displaying content based on the state of a form element, and much, much more.

These examples showcase both how to build forms with KendoReact components as well as with native HTML elements, so the guidance provided here can be used by anyone.

We hope that you will find this guide useful! Now, let's kick things off by diving into various form controls.

Form Components

Forms consist of form components (such as inputs, buttons, checkboxes, dropdowns, color pickers), their labels, hints and error messages. KendoReact provides comprehensive support for building forms using the Form and Labels packages, along with sharing valuable best practices described in the following article.

Here’s an example of the the Form and Labels packages in action, so you can get an idea of how they work.

import React from 'react';
import ReactDOM from 'react-dom';
import { Form, Field, FormElement } from '@progress/kendo-react-form';
import { Button } from '@progress/kendo-react-buttons';

import {
    FormDatePicker, FormNumericTextBox, FormInput,
    FormCheckbox, FormMaskedTextBox, FormTextArea
} from './form-components.jsx';

import {
    termsValidator, emailValidator, nameValidator,
    phoneValidator, guestsValidator, nightsValidator,
    arrivalDateValidator
} from './validators.jsx'

const App = () => {
    const handleSubmit = (dataItem) => alert(JSON.stringify(dataItem, null, 2));
    return (
        <Form
            onSubmit={handleSubmit}
            render={(formRenderProps) => (
                <FormElement style={{width: 400}}>
                    <fieldset className={'k-form-fieldset'}>
                        <legend className={'k-form-legend'}>BOOK YOUR DREAM VACATION TODAY</legend>
                        <Field
                            id={'fullName'}
                            name={'fullName'}
                            label={'Full Name'}
                            component={FormInput}
                            validator={nameValidator}
                        />
                        <Field
                            id={'phoneNumber'}
                            name={'phoneNumber'}
                            label={'Phone Number'}
                            mask={'(999) 000-00-00-00'}
                            hint={'Hint: Your active phone number.'}
                            component={FormMaskedTextBox}
                            validator={phoneValidator}
                        />
                        <Field
                            id={'email'}
                            name={'email'}
                            label={'Email'}
                            hint={'Hint: Enter your personal email address.'}
                            type={'email'}
                            component={FormInput}
                            validator={emailValidator}
                        />
                        <div style={{display: 'flex', justifyContent: 'space-between'}}>
                            <Field
                                id={'arrivalDate'}
                                name={'arrivalDate'}
                                label={'Arrival Date'}
                                hint={'Hint: Should be greater than today'}
                                component={FormDatePicker}
                                validator={arrivalDateValidator}
                                wrapperStyle={{width: '90%', marginRight: '18px'}}
                            />
                            <Field
                                id={'nightsCount'}
                                name={'nightsCount'}
                                label={'Number of Nights'}
                                format={'n0'}
                                component={FormNumericTextBox}
                                validator={nightsValidator}
                            />
                        </div>
                        <Field
                            id={'guestsCount'}
                            name={'guestsCount'}
                            label={'Number of Guests'}
                            hint={'Hint: Maximum 5 guests.'}
                            format={'n0'}
                            component={FormNumericTextBox}
                            validator={guestsValidator}
                        />
                        <Field
                            id={'comments'}
                            name={'comments'}
                            label={'Comments'}
                            optional={true}
                            component={FormTextArea}
                        />
                        <span className={'k-form-separator'} />
                        <Field
                            id={'terms'}
                            name={'terms'}
                            label={'I agree with terms and conditions'}
                            component={FormCheckbox}
                            validator={termsValidator}
                        />
                        <div className="k-form-buttons">
                            <Button
                                primary={true}
                                type={'submit'}
                                disabled={!formRenderProps.allowSubmit}
                            >
                                Send Reservation Request
                            </Button>
                            <Button onClick={formRenderProps.onFormReset}>
                                Clear
                            </Button>
                        </div>
                    </fieldset>
                </FormElement>
            )}
        />
    );
};
ReactDOM.render(
    <App />,
    document.querySelector('my-app')
);

KendoReact Form Package

The React Form package provides components for form state management, form validation and creating form layouts. For form state management the Form package uses the following components:

For creating form layouts the Form package provides following components:

  • FormElement component enables creating horizontal and vertical form layouts. It's designed to work with the FieldWrapper component, components from the Labels package and any editor.
  • FieldWrapper component enable you to group, align and control form-related content, such as KendoReact components or HTML input elements.

Except for components representing a group (such as radio buttons), each FieldWrapper component can contain a single component element, a label and multiple hint or error messages.

React Form Inputs

The following example demonstrates the KendoReact Inputs within a form in action:

import React from 'react';
import ReactDOM from 'react-dom';
import { Form, Field, FormElement } from '@progress/kendo-react-form';
import { Button } from '@progress/kendo-react-buttons';

import {
    FormNumericTextBox, FormInput,
    FormMaskedTextBox, FormColorPicker,
    FormSwitch, FormSlider
} from './form-components.jsx';

import {
    nameValidator, colorValidator, phoneValidator,
} from './validators.jsx'

const App = () => {
    const handleSubmit = (dataItem) => alert(JSON.stringify(dataItem, null, 2));
    return (
        <Form
            onSubmit={handleSubmit}
            initialValues={{
                amount: 0
            }}
            render={(formRenderProps) => (
                <FormElement style={{width: 400}}>
                    <Field
                        id={'firstName'}
                        name={'firstName'}
                        label={'First Name'}
                        component={FormInput}
                        validator={nameValidator}
                    />
                    <Field
                        id={'phoneNumber'}
                        name={'phoneNumber'}
                        label={'Phone Number'}
                        hint={'Hint: Your active phone number.'}
                        mask={'(999) 000-00-00-00'}
                        component={FormMaskedTextBox}
                        validator={phoneValidator}
                    />
                    <Field
                        id={'amount'}
                        name={'amount'}
                        label={'Amount'}
                        hint={'Hint: Amount of money.'}
                        format={'n2'}
                        component={FormNumericTextBox}
                    />
                    <Field
                        id={'color'}
                        name={'color'}
                        label={'Choose Color'}
                        component={FormColorPicker}
                        validator={colorValidator}
                    />
                    <Field
                        id={'priceLimit'}
                        name={'priceLimit'}
                        label={'Price Limit'}
                        hint={'Hint: Choose a price limit'}
                        min={1}
                        max={10}
                        min={1}
                        component={FormSlider}
                        data={[ 1, 3, 5, 6, 10 ]}
                    />
                    <Field
                        id={'notifications'}
                        name={'notifications'}
                        label={'Allow notifications'}
                        component={FormSwitch}
                        optional={true}
                    />
                    <div className="k-form-buttons">
                        <Button
                            primary={true}
                            type={'submit'}
                            disabled={!formRenderProps.allowSubmit}
                        >
                            Submit
                        </Button>
                        <Button onClick={formRenderProps.onFormReset}>
                            Clear
                        </Button>
                    </div>
                </FormElement>
            )}
        />
    );
};
ReactDOM.render(
    <App />,
    document.querySelector('my-app')
);

Checkboxes & RadioButtons

The following example demonstrates the KendoReact Checkbox and RadioGroup components within a form in action:

import React from 'react';
import ReactDOM from 'react-dom';
import { Form, Field, FormElement } from '@progress/kendo-react-form';
import { Button } from '@progress/kendo-react-buttons';

import {
    FormCheckbox,  FormRadioGroup
} from './form-components.jsx';

const confirmationData = [
    { label: 'Phone Call', value: 'phone'},
    { label: 'Via Email', value: 'email'}
];

const genderData = [
    { label: 'Male', value: 'male'},
    { label: 'Female', value: 'female'},
    { label: 'Other', value: 'other'}
];

const App = () => {
    const handleSubmit = (dataItem) => alert(JSON.stringify(dataItem, null, 2));
    return (
        <Form
            onSubmit={handleSubmit}
            render={(formRenderProps) => (
                <FormElement style={{width: 400}}>
                    <Field
                        id={'confirmationType'}
                        name={'confirmationType'}
                        label={'Type of Confirmation'}
                        hint={'Hint: Choose a way to receive a confirmation'}
                        component={FormRadioGroup}
                        data={confirmationData}
                    />
                     <Field
                        id={'gender'}
                        name={'gender'}
                        label={'Gender'}
                        layout={'horizontal'}
                        component={FormRadioGroup}
                        data={genderData}
                    />
                    <Field
                        id={'terms'}
                        name={'terms'}
                        label={'I agree with terms and conditions'}
                        hint={'Hint: By checking this, you agree to our Terms&Conditions'}
                        component={FormCheckbox}
                    />
                    <div className="k-form-buttons">
                        <Button
                            primary={true}
                            type={'submit'}
                            disabled={!formRenderProps.allowSubmit}
                        >
                            Submit
                        </Button>
                        <Button onClick={formRenderProps.onFormReset}>
                            Clear
                        </Button>
                    </div>
                </FormElement>
            )}
        />
    );
};
ReactDOM.render(
    <App />,
    document.querySelector('my-app')
);

Uploads

The following example demonstrates the KendoReact Upload within a form in action:

import React from 'react';
import ReactDOM from 'react-dom';
import { Form, Field, FormElement } from '@progress/kendo-react-form';
import { Button } from '@progress/kendo-react-buttons';

import {
    FormUpload
} from './form-components.jsx';

const App = () => {
    const handleSubmit = (dataItem) => alert(JSON.stringify(dataItem, null, 2));
    return (
        <Form
            onSubmit={handleSubmit}
            render={(formRenderProps) => (
                <FormElement style={{width: 400}}>
                    <Field
                        id={'avatar'}
                        name={'avatar'}
                        label={'Avatar'}
                        hint={'Hint: Upload your avatar picture'}
                        component={FormUpload}
                    />
                    <Field
                        id={'photos'}
                        name={'photos'}
                        label={'Upload Photos'}
                        hint={'Hint: Select your additional photos'}
                        component={FormUpload}
                    />
                    <div className="k-form-buttons">
                        <Button
                            primary={true}
                            type={'submit'}
                            disabled={!formRenderProps.allowSubmit}
                        >
                            Submit
                        </Button>
                        <Button onClick={formRenderProps.onFormReset}>
                            Clear
                        </Button>
                    </div>
                </FormElement>
            )}
        />
    );
};
ReactDOM.render(
    <App />,
    document.querySelector('my-app')
);

DropDowns

The following example demonstrates the KendoReact DropDowns within a form in action:

import React from 'react';
import ReactDOM from 'react-dom';
import { Form, Field, FormElement } from '@progress/kendo-react-form';
import { Button } from '@progress/kendo-react-buttons';

import {
    FormDropDownList, FormAutoComplete,
    FormComboBox, FormMultiSelect
} from './form-components.jsx';

import {
    requiredValidator
} from './validators.jsx'

import {
    countries, genders, sizes, sports
} from './data.jsx'

const App = () => {
    const handleSubmit = (dataItem) => alert(JSON.stringify(dataItem, null, 2));
    return (
        <Form
            onSubmit={handleSubmit}
            render={(formRenderProps) => (
                <FormElement style={{width: 400}}>
                    <Field
                        id={'countryselected'}
                        name={'countryselected'}
                        label={'Country'}
                        hint={'Hint: Only Eroupean countries'}
                        component={FormAutoComplete}
                        data={countries}
                        validator={requiredValidator}
                    />
                    <Field
                        id={'genderseleceted'}
                        name={'genderselected'}
                        label={'Gender'}
                        component={FormComboBox}
                        textField={'text'}
                        data={genders}
                        validator={requiredValidator}
                    />
                    <Field
                        id={'size'}
                        name={'size'}
                        label={'T-Shirt Size'}
                        component={FormDropDownList}
                        data={sizes}
                        validator={requiredValidator}
                    />
                    <Field
                        id={'sport'}
                        name={'sport'}
                        label={'Sport'}
                        hint={'Hint: Your favourite sport'}
                        component={FormMultiSelect}
                        data={sports}
                        validator={requiredValidator}
                    />
                    <div className="k-form-buttons">
                        <Button
                            primary={true}
                            type={'submit'}
                            disabled={!formRenderProps.allowSubmit}
                        >
                            Submit
                        </Button>
                        <Button onClick={formRenderProps.onFormReset}>
                            Clear
                        </Button>
                    </div>
                </FormElement>
            )}
        />
    );
};
ReactDOM.render(
    <App />,
    document.querySelector('my-app')
);

DateInputs

The following example demonstrates the KendoReact DateInputs within a form in action:

import React from 'react';
import ReactDOM from 'react-dom';
import { Form, Field, FormElement } from '@progress/kendo-react-form';
import { Button } from '@progress/kendo-react-buttons';

import {
    FormDatePicker, FormTimePicker,
    FormDateTimePicker, FormDateInput,
    FormDateRangePicker
} from './form-components.jsx';

import {
    requiredValidator
} from './validators.jsx'

const App = () => {
    const handleSubmit = (dataItem) => alert(JSON.stringify(dataItem, null, 2));
    return (
        <Form
            onSubmit={handleSubmit}
            render={(formRenderProps) => (
                <FormElement style={{width: 400}}>
                    <Field
                        id={'appointment'}
                        name={'appointment'}
                        label={'Appointment Date and Time'}
                        hint={'Hint: Select Date and Time for your appointment'}
                        component={FormDateTimePicker}
                        validator={requiredValidator}
                    />
                    <Field
                        id={'bdate'}
                        name={'bdate'}
                        label={'Birth Date'}
                        component={FormDateInput}
                        validator={requiredValidator}
                    />
                    <Field
                        id={'btime'}
                        name={'btime'}
                        label={'Time of Birth'}
                        hint={'Hint: Select your time of birth'}
                        component={FormTimePicker}
                        validator={requiredValidator}
                    />
                    <Field
                        id={'gdate'}
                        name={'gdate'}
                        label={'Date of Graduation'}
                        component={FormDatePicker}
                        validator={requiredValidator}
                    />
                    <Field
                        id={'subscriptionDate'}
                        name={'subscriptionDate'}
                        label={'Subscription'}
                        component={FormDateRangePicker}
                        validator={requiredValidator}
                    />
                    <div className="k-form-buttons">
                        <Button
                            primary={true}
                            type={'submit'}
                            disabled={!formRenderProps.allowSubmit}
                        >
                            Submit
                        </Button>
                        <Button onClick={formRenderProps.onFormReset}>
                            Clear
                        </Button>
                    </div>
                </FormElement>
            )}
        />
    );
};
ReactDOM.render(
    <App />,
    document.querySelector('my-app')
);

Sizing

By default, all form components nested in a FormElement will take 100% of their parent element’s width, except for the ColorPicker, Switch and Slider components.

Labeling

Labels are not just visually associated with form elements, but programmatically as well. Assistive technologies will read the label content when the form element is focused. Additionally, on label click, the form element will receive focus and click, providing an increased hit area to activate it.

Labels

The Label component associates a label with a component using its props. When the form component is a plain HTML element or simple component like Input, only the editorId prop of the Label and id prop of the editor is used to associate the pair. For complex components without a form element, additional properties are required to enable screen readers to correctly read the label and forward the focus and click events. Refer to the Label overview for further details and runnable demos.

Floating Labels

Тhe FloatingLabel is an inline label above the input when the user has focused the form field or has entered a value. One of its benefits is that it looks good, and the resolved animation when moving to the next field gives users a sense of completion.

import React from 'react';
import ReactDOM from 'react-dom';
import { Form, Field, FormElement } from '@progress/kendo-react-form';
import { Button } from '@progress/kendo-react-buttons';

import {
    FormMaskedTextBox, FormFloatingNumericTextBox
} from './form-components.jsx';

import {
    requiredValidator, phoneValidator
} from './validators.jsx'

const App = () => {
    const handleSubmit = (dataItem) => alert(JSON.stringify(dataItem, null, 2));
    return (
        <Form
            onSubmit={handleSubmit}
            render={(formRenderProps) => (
                <FormElement style={{width: 400}}>
                    <Field
                        id={'age'}
                        name={'age'}
                        label={'Age'}
                        hint={'Hint: Enter your age.'}
                        format={'n2'}
                        component={FormFloatingNumericTextBox}
                        validator={requiredValidator}
                    />
                    <Field
                        id={'street'}
                        name={'street'}
                        label={'Street Number'}
                        hint={'Hint: Enter your street number.'}
                        format={'n2'}
                        component={FormFloatingNumericTextBox}
                        validator={requiredValidator}
                    />
                    <div className="k-form-buttons">
                        <Button
                            primary={true}
                            type={'submit'}
                            disabled={!formRenderProps.allowSubmit}
                        >
                            Submit
                        </Button>
                        <Button onClick={formRenderProps.onFormReset}>
                            Clear
                        </Button>
                    </div>
                </FormElement>
            )}
        />
    );
};
ReactDOM.render(
    <App />,
    document.querySelector('my-app')
);

Optional Labels

The way we visualize a form field has a strong implication for how users perceive and complete forms. As an example, instead of marking every field as required you can only mark the optional fields. This results in less visual noise, since there will be fewer red marks across your user interface. This will lead to users completing the form faster.

Both the Label and FloatingLabel components have an optional Boolean property, which if set to true will render “(Optional)” text inside the label element of the form component. The text is localizable via the KendoReact Localization package. Refer to the article on Globalization for further details and runnable demos.

import React from 'react';
import ReactDOM from 'react-dom';
import { Form, Field, FormElement } from '@progress/kendo-react-form';
import { Button } from '@progress/kendo-react-buttons';

import {
    FormMaskedTextBox, FormFloatingNumericTextBox
} from './form-components.jsx';

import {
    requiredValidator, phoneValidator
} from './validators.jsx'

const App = () => {
    const handleSubmit = (dataItem) => alert(JSON.stringify(dataItem, null, 2));
    return (
        <Form
            onSubmit={handleSubmit}
            render={(formRenderProps) => (
                <FormElement style={{width: 400}}>
                    <Field
                        id={'street'}
                        name={'street'}
                        label={'Street Number'}
                        hint={'Hint: Enter your street number.'}
                        optional={true}
                        format={'n2'}
                        component={FormFloatingNumericTextBox}
                    />
                    <Field
                        id={'phoneNumber'}
                        name={'phoneNumber'}
                        label={'Phone Number'}
                        hint={'Hint: Your active phone number.'}
                        mask={'(999) 000-00-00-00'}
                        optional={true}
                        component={FormMaskedTextBox}
                    />
                    <div className="k-form-buttons">
                        <Button
                            primary={true}
                            type={'submit'}
                            disabled={!formRenderProps.allowSubmit}
                        >
                            Submit
                        </Button>
                        <Button onClick={formRenderProps.onFormReset}>
                            Clear
                        </Button>
                    </div>
                </FormElement>
            )}
        />
    );
};
ReactDOM.render(
    <App />,
    document.querySelector('my-app')
);

Placeholder Texts

The placeholder text typically has a lighter color treatment to indicate that it is a suggestion for what kind of input will be valid.

import React from 'react';
import ReactDOM from 'react-dom';
import { Form, Field, FormElement } from '@progress/kendo-react-form';
import { Button } from '@progress/kendo-react-buttons';

import {
    FormMaskedTextBox,  FormInput
} from './form-components.jsx';

import {
    requiredValidator, cardValidator, cvcValidator
} from './validators.jsx'

const App = () => {
    const handleSubmit = (dataItem) => alert(JSON.stringify(dataItem, null, 2));
    return (
        <Form
            onSubmit={handleSubmit}
            render={(formRenderProps) => (
                <FormElement style={{width: 400}} horizontal={true}>
                    <Field
                        id={'fullName'}
                        name={'fullName'}
                        label={'Full Name'}
                        placeholder={'e.g.: Clevey Thursfield'}
                        component={FormInput}
                        validator={requiredValidator}
                    />
                    <div className="k-form-buttons">
                        <Button
                            primary={true}
                            type={'submit'}
                            disabled={!formRenderProps.allowSubmit}
                        >
                            Submit
                        </Button>
                        <Button onClick={formRenderProps.onFormReset}>
                            Clear
                        </Button>
                    </div>
                </FormElement>
            )}
        />
    );
};
ReactDOM.render(
    <App />,
    document.querySelector('my-app')
);

Hint Messages

Hint messages provide additional context about the expected value of a form component. For example, in a registration form where users have to provide a password containing at least 3 characters, a hint message aligned under the input could avoid any confusion. To associate the Hint message with the editor for screen readers, set the id property of the component to the ariaDescribedBy property of the editor.

import React from 'react';
import ReactDOM from 'react-dom';
import { Form, Field, FormElement } from '@progress/kendo-react-form';
import { Button } from '@progress/kendo-react-buttons';

import {
    FormInput, FormDatePicker
} from './form-components.jsx';

import {
    requiredValidator
} from './validators.jsx'

const App = () => {
    const handleSubmit = (dataItem) => alert(JSON.stringify(dataItem, null, 2));
    return (
        <Form
            onSubmit={handleSubmit}
            render={(formRenderProps) => (
                <FormElement style={{width: 400}}>
                    <Field
                        id={'bdate'}
                        name={'bdate'}
                        label={'Birth Date'}
                        hint={'Hint: Hint message aligned end'}
                        hintDirection={'end'}
                        component={FormDatePicker}
                        validator={requiredValidator}
                    />
                     <Field
                        id={'username'}
                        name={'username'}
                        label={'User Name'}
                        hint={'Hint: Hint message aligned start'}
                        component={FormInput}
                        validator={requiredValidator}
                    />
                    <div className="k-form-buttons">
                        <Button
                            primary={true}
                            type={'submit'}
                            disabled={!formRenderProps.allowSubmit}
                        >
                            Submit
                        </Button>
                        <Button onClick={formRenderProps.onFormReset}>
                            Clear
                        </Button>
                    </div>
                </FormElement>
            )}
        />
    );
};
ReactDOM.render(
    <App />,
    document.querySelector('my-app')
);

React Form Validation

Form validation is used to make sure the user has provided correct information in terms of format, content length, etc. For example, is the phone number an actual number, did the user put info in all of the required fields, and so on.

How it Works?

KendoReact supports handling user input in the UI, and displays useful validation messages using the Form, Field and Error components. Refer to the article on form validation for further details and runnable demos.

Field-level Form Validation

Field-level validation ensures that the value entered in the field is in accordance with the application requirements. If the validation rules are not satisfied, error messages for each field are displayed.

To keep the form UI clean, the error messages of an invalid component are usually not shown until specific user interaction. There are several states of the edited field which can be used to display a validation message:

  • The component was edited—modified state
  • The component was focused—visited state
  • The component was blurred—touched state
  • Aways—Show error messages regardless of user interaction

The Field component and its FieldRenderProps enable you to control when and how validation messages will be shown.

Error Messages

For instant validation, error messages are the best way to alert users that they have made a mistake while filling out a form. Applying only error-specific styles does not convey enough information about what the user should do to provide valid data. Error messages should specify exactly why the user input is not accepted and should be shown one for each field at a time. KendoReact provides an Error component for that use. To associate it with the editor for screen readers, set the id property of the component to the ariaDescribedBy property of the editor.

import React from 'react';
import ReactDOM from 'react-dom';
import { Form, Field, FormElement } from '@progress/kendo-react-form';
import { Button } from '@progress/kendo-react-buttons';

import {
    FormInput, FormDatePicker
} from './form-components.jsx';

import {
    userNameValidator, emailValidator
} from './validators.jsx'

const App = () => {
    const handleSubmit = (dataItem) => alert(JSON.stringify(dataItem, null, 2));
    return (
        <Form
            onSubmit={handleSubmit}
            render={(formRenderProps) => (
                <FormElement style={{width: 400}}>
                     <Field
                        id={'username'}
                        name={'username'}
                        label={'User Name'}
                        component={FormInput}
                        validator={userNameValidator}
                    />
                    <Field
                        id={'email'}
                        name={'email'}
                        label={'Email'}
                        type={'email'}
                        component={FormInput}
                        validator={emailValidator}
                    />
                    <div className="k-form-buttons">
                        <Button
                            primary={true}
                            type={'submit'}
                            disabled={!formRenderProps.allowSubmit}
                        >
                            Submit
                        </Button>
                        <Button onClick={formRenderProps.onFormReset}>
                            Clear
                        </Button>
                    </div>
                </FormElement>
            )}
        />
    );
};
ReactDOM.render(
    <App />,
    document.querySelector('my-app')
);