Skip to content

Controller

Wrapper component for controlled inputs

Controller: Component

React Hook Form embraces uncontrolled components and native inputs, however it's hard to avoid working with external controlled component such as React-Select, AntD and Material-UI. This wrapper component will make it easier for you to work with them.

Props

The following table contains information about all the arguments for useController.

NameTypeRequiredDescription
namestringUnique name of your input.
controlObjectcontrol object is from invoking useForm. Optional when using FormProvider.
renderFunction

This is a render prop. A function that returns a React element and provides the ability to attach events and value into the component. This simplifies integrating with external controlled components with non-standard prop names. Provides onChange, onBlur, name, ref and value to the child component, and also a fieldState object which contains specific input state.

<Controller
  control={control}
  name="test"
  render={({
    field: { onChange, onBlur, value, name, ref },
    fieldState: { invalid, isTouched, isDirty, error },
    formState,
  }) => (
    <Checkbox
      onBlur={onBlur}
      onChange={onChange}
      checked={value}
      inputRef={ref}
    />
  )}
/>
<Controller
  render={({
    field: { onChange, onBlur, value, name, ref },
    fieldState: { invalid, isTouched, isDirty, error },
  }) => (
    <TextField
      value={props.value}
      onChange={props.onChange}
      inputRef={props.ref} // wire up the input ref
    />
  )}
  name="TextField"
  control={control}
  rules={{ required: true }}
/>
defaultValueanyThe same as an uncontrolled component's defaultValue. When passing a boolean value, it will be treated as checkbox input. For more details, see useForm's defaultValues section.
  • You need to either set defaultValue at the field-level or call useForm with defaultValues.

  • If your form will invoke reset with default values, you will need to call useForm with defaultValues instead of setting the defaultValue on individual fields.

rulesObjectValidation rules in the same format as for register.
rules={{ required: true }}

Return

The following table contains information about properties which Controller produce.

React.RefObject
Object NameNameTypeDescription
fieldonChange(value: any) => void

A function which send value to hook form and should be assigned with onChange prop.

onBlur(value: any) => void

A function which send value to hook form and should be assigned with onBlur prop.

valueunknown

The value of this controlled component.

ref

Assign ref to component's input ref, so hook form can focus on the error input.

fieldStateinvalidboolean

Invalid state for current input.

isTouchedboolean

Touched state for current controlled input.

isDirtyboolean

Dirty state for current controlled input.

errorobject

error for this specific input.

formStateisSubmitSuccessfulboolean

Indicate the form was successfully submitted.

isDirtyboolean

Set to true after the user modifies any of the inputs.

isSubmittedboolean

Set to true after the form is submitted. Will remain true until the reset method is invoked.

dirtyFieldsobject

An object with the user-modified fields. Make sure to provide all inputs' defaultValues at the useForm, so hook form can compare with the defaultValue.

touchedFieldsobject

An object containing all the inputs the user has interacted with.

isSubmittingboolean

true if the form is currently being submitted. false if otherwise.

submitCountnumber

Number of times the form was submitted.

isValidboolean

Set to true if the form doesn't have any errors.

isValidatingboolean

Set to true during validation.

import React from "react";
import ReactDatePicker from "react-datepicker";
import { TextField } from "@material-ui/core";
import { useForm, Controller } from "react-hook-form";

function App() {
  const { handleSubmit, control } = useForm();

  return (
    <form onSubmit={handleSubmit(data => console.log(data))}>
      <Controller
        control={control}
        name="ReactDatepicker"
        render={({ field: { onChange, onBlur, value, ref } }) => (
          <ReactDatePicker
            onChange={onChange}
            onBlur={onBlur}
            selected={value}
          />
        )}
      />
      
      <input type="submit" />
    </form>
  );
}
import React from "react";
import ReactDatePicker from "react-datepicker";
import { TextField } from "@material-ui/core";
import { useForm, Controller } from "react-hook-form";

type FormValues = {
  ReactDatepicker: string;
} 

function App() {
  const { handleSubmit, control } = useForm<FormValues>();

  return (
    <form onSubmit={handleSubmit(data => console.log(data))}>
      <Controller
        control={control}
        name="ReactDatepicker"
        render={({ field: { onChange, onBlur, value, ref } }) => (
          <ReactDatePicker
            onChange={onChange}
            onBlur={onBlur}
            selected={value}
          />
        )}
      />
      
      <input type="submit" />
    </form>
  );
}
Expo
import React from "react";
import { Text, View, TextInput, Button, Alert } from "react-native";
import { useForm, Controller } from "react-hook-form";

export default function App() {
  const { control, handleSubmit, formState: { errors } } = useForm();
  const onSubmit = data => console.log(data);

  return (
    <View>
      <Controller
        control={control}
        render={({ field: { onChange, onBlur, value } }) => (
          <TextInput
            style={styles.input}
            onBlur={onBlur}
            onChangeText={value => onChange(value)}
            value={value}
          />
        )}
        name="firstName"
        rules={{ required: true }}
        defaultValue=""
      />
      {errors.firstName && <Text>This is required.</Text>}

      <Controller
        control={control}
        render={({ field: { onChange, onBlur, value } }) => (
          <TextInput
            style={styles.input}
            onBlur={onBlur}
            onChangeText={value => onChange(value)}
            value={value}
          />
        )}
        name="lastName"
        defaultValue=""
      />

      <Button title="Submit" onPress={handleSubmit(onSubmit)} />
    </View>
  );
}

Tips

  • Do not register input again. This component is made to take care the registration process.

    <Controller 
      name="test"
      render={({ field }) => {
        // return <input {...field} {...register('test')} />; ❌ double up the registration  
        return <input {...field} />; // ✅  
      }} 
    />        
    
  • Customise what value gets send to hook form by transform the value during onChange.

    <Controller 
      name="test"
      render={({ field }) => {
        // sending integer instead of string.
        return <input {...field} onChange={(e) => field.onChange(parseInt(e.target.value))} />;  
      }} 
    />    
    
Edit