Skip to main content

React Hook Form

Setup​

  1. Run this command

    npm install react-hook-form@7
  2. Add the script

    index.html​

    ...
    <script src="/node_modules/react/umd/react.development.js"></script>
    <script src="/node_modules/react-dom/umd/react-dom.development.js"></script>
    + <script src="/node_modules/react-hook-form/dist/index.umd.js"></script>
    <script src="/node_modules/@babel/standalone/babel.min.js"></script>
    ...
  3. Add this code

    main.js​

    console.log(window.ReactHookForm);
  4. Open the Chrome DevTools to the console tab similar output to what is shown below

{Controller: ƒ, Form: ƒ, FormProvider: ƒ, appendErrors: ƒ, get: ƒ, …}

Getting Data from the Form​

Manually Using Controlled Components​

  • This is the same example from the prior Forms section provided here for easy comparison.
function SigninForm() {
const [username, setUsername] = React.useState('');
const [password, setPassword] = React.useState('');

const handleSubmit = (event) => {
event.preventDefault();
console.log(username, password);
};

return (
<form onSubmit={handleSubmit}>
<input
type="text"
name="username"
value={username}
onChange={(event) => setUsername(event.target.value)}
/>
<input
type="password"
name="password"
value={password}
onChange={(event) => setPassword(event.target.value)}
/>
<button type="submit">Sign In</button>
</form>
);
}

ReactDOM.createRoot(document.getElementById('root')).render(<SigninForm />);

Using React Hook Form​

  1. Run this code

    const { useForm } = window.ReactHookForm;

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

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

    console.log({ ...register('username') });

    return (
    <form onSubmit={handleSubmit(signIn)}>
    <input type="text" {...register('username')} placeholder="Username" />
    <input
    type="password"
    {...register('password')}
    placeholder="Password"
    />
    <button type="submit">Sign In</button>
    </form>
    );
    }

    ReactDOM.createRoot(document.getElementById('root')).render(<App />);
  2. Add this log statement to see what the React Hook Form library is doing for you.

    const { useForm } = window.ReactHookForm;

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

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

    + console.log({ ...register("username") });

    return (
    <form onSubmit={handleSubmit(signIn)}>
    <input
    type="text"
    {...register("username")}
    placeholder="Username"
    />
    <input
    type="password"
    {...register("password")}
    placeholder="Password"
    />
    <button type="submit">Sign In</button>
    </form>
    );
    }

    ReactDOM.createRoot(document.getElementById("root")).render(<App />);

  3. Open the Chrome Devtools and view the Console output

{name: 'username', onChange: ƒ, onBlur: ƒ, ref: ƒ}

Do you see how it's doing what we did manually previously?

Contact Us Form​

const { useForm } = window.ReactHookForm;
function ContactUsForm() {
const { register, handleSubmit, watch } = useForm();

function send(data) {
console.log(data);
}

return (
<form onSubmit={handleSubmit(send)}>
<select {...register('department')}>
<option value="">Select...</option>
<option value="hr">Human Resources</option>
<option value="pr">Public Relations</option>
<option value="support">Support</option>
</select>
<br />
<p className="alert"></p>
<br />
<textarea {...register('message')} cols="30" rows="10" />
<br />
<input {...register('agreeToTerms')} type="checkbox" />
I agree to the terms and conditions.
<br />
<button>Send</button>
<pre>{JSON.stringify(watch())}</pre>
</form>
);
}

ReactDOM.createRoot(document.getElementById('root')).render(<ContactUsForm />);

Tip: The watch function causes the component to render again. Use getValues if you don't want to trigger the component to render again (which is most use cases).

Validation​

Contact Us Form with Validation​

const { useForm } = window.ReactHookForm;

function ContactUsForm() {
const {
register,
handleSubmit,
formState: { errors },
} = useForm({
mode: 'onTouched', // Validate when the field is touched and loses focus
reValidateMode: 'onChange', // After an error appears, update it on every keystroke
});

function send(data) {
console.log(data);
}

return (
<form onSubmit={handleSubmit(send)}>
<select
{...register('department', { required: 'Department is required' })}
>
<option value="">Select...</option>
<option value="hr">Human Resources</option>
<option value="pr">Public Relations</option>
<option value="support">Support</option>
</select>
<br />
{errors.department && (
<p className="alert">{errors.department?.message}</p>
)}
<br />
<textarea
{...register('message', { required: 'A message is required' })}
cols="30"
rows="10"
/>
<br />
{errors.message && <p className="alert">{errors.message?.message}</p>}
<br />
<input
{...register('agreeToTerms', {
required: 'You must accept the terms to send',
})}
type="checkbox"
/>
I agree to the terms and conditions.
<br />
{errors.agreeToTerms && (
<p className="alert">{errors.agreeToTerms?.message}</p>
)}
<br />
<button>Send</button>
</form>
);
}

ReactDOM.createRoot(document.getElementById('root')).render(<ContactUsForm />);

Loading Default Values​

This section explores how to load initial values from an object including how to load them asynchronously when communicating with an API to retrieve the data.

Default Values​

const { useForm } = window.ReactHookForm;
function ContactUsForm() {
const { register, handleSubmit } = useForm({
defaultValues: {
department: 'support',
message: 'I need a refund.',
agreeToTerms: true,
},
});

function send(data) {
console.log(data);
}

return (
<form onSubmit={handleSubmit(send)}>
<select {...register('department')}>
<option value="">Select...</option>
<option value="hr">Human Resources</option>
<option value="pr">Public Relations</option>
<option value="support">Support</option>
</select>
<br />
<p className="alert"></p>
<br />
<textarea {...register('message')} cols="30" rows="10" />
<br />
<input {...register('agreeToTerms')} type="checkbox" />
I agree to the terms and conditions.
<br />
<button>Send</button>
</form>
);
}

ReactDOM.createRoot(document.getElementById('root')).render(<ContactUsForm />);

Default Values from API (Async)​

const { useForm } = window.ReactHookForm;

let contact = {
department: 'support',
message: 'I need a refund.',
agreeToTerms: true,
};

let contactAPI = {
find(id) {
//calling api here in the real world
return Promise.resolve(contact);
},
};

function ContactUsForm({ id }) {
const { register, handleSubmit } = useForm({
defaultValues: async () => {
let contact = await contactAPI.find(id);
return contact;
},
});

function send(data) {
console.log(data);
}

return (
<form onSubmit={handleSubmit(send)}>
<select {...register('department')}>
<option value="">Select...</option>
<option value="hr">Human Resources</option>
<option value="pr">Public Relations</option>
<option value="support">Support</option>
</select>
<br />
<p className="alert"></p>
<br />
<textarea {...register('message')} cols="30" rows="10" />
<br />
<input {...register('agreeToTerms')} type="checkbox" />
I agree to the terms and conditions.
<br />
<button>Send</button>
</form>
);
}

ReactDOM.createRoot(document.getElementById('root')).render(<ContactUsForm />);

Reference​