React Hook Form

It’s possible to build forms using Forma 36’s components and React Hook Form. In this page we will show a couple of examples on how to do that.

Import

import { useForm } from 'react-hook-form';
import {
Form,
FormControl,
Radio,
Checkbox,
Select,
Textarea,
TextInput,
} from '@contentful/f36-forms';

Examples

Example of integration with register()

function RegisterIntegrationExample() {
  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm();

  // Create your submit function that will be passed to react-hook-form’s handleSubmit
  const onSubmit = (data) => {
    console.log(data);
  };

  /* Pass react-hook-form’s handleSubmit to the onSubmit prop of the Form component  */
  return (
    <Form onSubmit={handleSubmit(onSubmit)}>
      {/* To set the FormControl invalid state, check if there is an error related to this input  */}
      <FormControl isInvalid={Boolean(errors.name)}>
        <FormControl.Label isRequired>Name</FormControl.Label>

        {/* Use the register function from react-hook-form on TextInput */}
        <TextInput {...register('name', { required: true })} />

        {/* Show a ValidationMessage if the input has any errors */}
        {errors.name && (
          <FormControl.ValidationMessage>
            This field is required
          </FormControl.ValidationMessage>
        )}
      </FormControl>

      <FormControl isInvalid={Boolean(errors.description)}>
        <FormControl.Label>Description</FormControl.Label>

        {/* Registering a Textarea is similar to TextInput */}
        <Textarea {...register('description', { maxLength: 20 })} />

        {errors.description && (
          <FormControl.ValidationMessage>
            Your description must have a maximum of 20 characters
          </FormControl.ValidationMessage>
        )}
      </FormControl>

      <FormControl>
        <FormControl.Label>City</FormControl.Label>

        {/* When using a Select, it’s only necessary to register the Select component */}
        <Select defaultValue="Mumbai" {...register('city')}>
          <Select.Option value="Cape Town">Cape Town</Select.Option>
          <Select.Option value="Mumbai">Mumbai</Select.Option>
          <Select.Option value="Rio de Janeiro">Rio de Janeiro</Select.Option>
        </Select>
      </FormControl>

      <FormControl as="fieldset">
        <FormControl.Label as="legend">Martial status</FormControl.Label>

        {/*
          When using Radio.Group, it’s necessary to register each Radio with the same "name".
          This is necessary because of how react-hook-form works.
        */}
        <Radio.Group defaultValue="single">
          <Radio value="married" {...register('martial-status')}>
            Married
          </Radio>
          <Radio value="single" {...register('martial-status')}>
            Single
          </Radio>
        </Radio.Group>
      </FormControl>

      <FormControl isInvalid={Boolean(errors.checkbox)}>
        <Checkbox {...register('checkbox', { required: true })}>
          I confirm everything that said above is true
        </Checkbox>

        {errors.checkbox && (
          <FormControl.ValidationMessage>
            You must confirm that everything is true
          </FormControl.ValidationMessage>
        )}
      </FormControl>

      <Button variant="primary" type="submit">
        Submit
      </Button>
    </Form>
  );
}

Example of integration with useController

First, it's necessary to create a controlled component which will use the useController hook and pass its props to the Forma 36 component. Then you use this controlled component in your form:

function UseControllerIntegrationExample() {
  const ControlledTextInput = (props) => {
    const {
      field: { ref, ...inputProps },
    } = useController(props);

    return <TextInput {...inputProps} ref={ref} />;
  };

  const {
    // instead of register, this time we use the control object returned by useForm()
    control,
    handleSubmit,
    formState: { errors },
  } = useForm();

  const onSubmit = (data) => {
    console.log(data);
  };

  return (
    <Form onSubmit={handleSubmit(onSubmit)}>
      <FormControl isInvalid={Boolean(errors.textInput)}>
        <FormControl.Label isRequired>Name</FormControl.Label>

        {/* Then we pass control to the controlled component as a prop */}
        <ControlledTextInput
          control={control}
          name="textInput"
          rules={{ required: true }}
          /**
           * We need to pass an empty string as default value
           * otherwise react will trigger the uncontrolled inputs warning
           *
           * more information in https://react-hook-form.com/api/usecontroller
           */
          defaultValue=""
        />

        {errors.textInput && (
          <FormControl.ValidationMessage>
            This field is required
          </FormControl.ValidationMessage>
        )}
      </FormControl>

      <Button variant="primary" type="submit">
        Submit
      </Button>
    </Form>
  );
}

Help improve this page