React Hook Form can submit form data without a page reload, even though it’s a client-side library.
Let’s see it in action. Imagine a simple contact form:
import { useForm } from 'react-hook-form';
function ContactForm() {
const { register, handleSubmit, formState: { errors } } = useForm();
const onSubmit = (data) => {
console.log(data);
// Here you'd typically send the data to an API
// For this example, we'll just log it
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div>
<label htmlFor="name">Name:</label>
<input
id="name"
{...register('name', { required: 'Name is required' })}
/>
{errors.name && <p>{errors.name.message}</p>}
</div>
<div>
<label htmlFor="email">Email:</label>
<input
id="email"
type="email"
{...register('email', {
required: 'Email is required',
pattern: {
value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i,
message: 'Invalid email address',
},
})}
/>
{errors.email && <p>{errors.email.message}</p>}
</div>
<button type="submit">Submit</button>
</form>
);
}
export default ContactForm;
When you fill out this form and click "Submit," the onSubmit function fires, and the data is logged to your browser’s console without the page refreshing. The magic happens in handleSubmit(onSubmit).
React Hook Form orchestrates form submissions by providing a handleSubmit function. This function is a higher-order function that takes your custom onSubmit handler as an argument. When the form’s onSubmit event is triggered (via the <form onSubmit={handleSubmit(onSubmit)}> prop), React Hook Form intercepts it. It first performs any validation you’ve configured using its built-in rules or schema validation. If validation passes, it then calls your provided onSubmit function, passing the validated form data as an argument. Crucially, React Hook Form also calls event.preventDefault() internally on the native form submission event, preventing the default browser behavior of a full page reload. This allows you to handle the submission asynchronously, perhaps by sending the data to a backend API using fetch or axios, and then updating the UI based on the API’s response.
The primary problem this solves is the clunky user experience of full page reloads on every form submission, which is a relic of traditional HTML forms. In modern web applications, we expect dynamic updates. React Hook Form enables this by allowing client-side handling of form data, including validation and submission, without interrupting the user’s flow. It abstracts away the manual DOM manipulation and event handling typically associated with forms in vanilla JavaScript, providing a declarative and efficient API. You register form inputs using the register function, which hooks them into React Hook Form’s state management and validation system. The handleSubmit function then acts as the gateway for submitting the collected, validated data.
When you register an input with register('fieldName'), React Hook Form is not just tracking the value. It’s also setting up the necessary event listeners (onChange, onBlur) on that input to update its internal state and trigger validation as needed. This means you don’t need to write onChange handlers for every input yourself to update component state. The formState object, destructured from the useForm hook, provides access to the current form state, including errors, isSubmitting, isValid, and more. This allows you to conditionally render error messages, disable the submit button while submitting, or show success feedback.
The handleSubmit function’s ability to prevent default form submission is a core feature, but what’s less obvious is how it manages the asynchronous nature of modern form submissions. When handleSubmit calls your onSubmit function, and that function itself returns a Promise (e.g., from an axios.post call), React Hook Form can track this. It sets the formState.isSubmitting flag to true while that Promise is pending. This is incredibly useful for providing visual feedback to the user, such as showing a loading spinner on the submit button, and for preventing duplicate submissions.
Understanding how handleSubmit wraps your onSubmit callback is key. It’s not just a simple pass-through. It performs validation first. If validation fails, it populates the errors object in formState and does not call your onSubmit function. If validation succeeds, it then calls your onSubmit with the cleaned data. This sequence is fundamental to how React Hook Form ensures data integrity before processing.
If you forget to return a Promise from your onSubmit handler when performing an asynchronous operation, React Hook Form won’t know that the submission is still in progress, and the isSubmitting state might not update correctly.
The next step after handling form submission is often managing server-side state and updating the UI accordingly.