Next.js - Form Validation Example with React Hook Form
Example built with Next.js 11.1.0 React Hook Form 7.12.1
Other versions available:
- Angular Reactive Forms: Angular 14, 10, 9, 8, 7, 6
- Angular Template-Driven Forms: Angular 14, 10, 9, 8, 7, 6
- Blazor: Blazor WebAssembly
- React + Formik: Formik 2, 1
- React Hook Form: React Hook Form 7, 6
- Vue + VeeValidate: Vue 3 Composition API, Vue 3 Options API, Vue 2
- Vue + Vuelidate: Vue 2
This is a quick example of how to setup form validation in Next.js with the React Hook Form library.
React Hook Form is a library for working with forms in React using React Hooks, I stumbled across it about a year ago and have been using it in my Next.js and React projects since then, I think it's easier to use than the other options available and requires less code. For more info see https://react-hook-form.com.
The example is a simple registration form with pretty standard fields for title, first name, last name, date of birth, email, password, confirm password and an accept terms and conditions checkbox. All fields are required including the checkbox, the dob must be a valid date, the email address must be in a valid format, the password field must have a min length of 6, and the confirm password and password fields must match.
Styling of the example is all done with Bootstrap 4.5 CSS, for more info see https://getbootstrap.com/docs/4.5/getting-started/introduction/.
Here it is in action: (See on CodeSandbox at https://codesandbox.io/s/next-js-form-validation-example-gy71d)
Next.js Home Page with React Hook Form
The home page contains an example registration form built with the React Hook Form library.
Form validation rules are defined with the Yup schema validation library and passed to the React Hook Form useForm()
function, for more info on Yup see https://github.com/jquense/yup.
Form validation rules are defined with the Yup schema validation library and passed with the formOptions
to the React Hook Form useForm()
function, for more info on Yup see https://github.com/jquense/yup.
The useForm()
hook function returns an object with methods for working with a form including registering inputs, handling form submit, resetting the form, accessing form state, displaying errors and more, for a complete list see https://react-hook-form.com/api/useform.
The onSubmit()
method is called when the form is valid and submitted, and simply displays the form data in a javascript alert.
The returned JSX template contains the form with all of the input fields and validation messages. The form fields are registered with the React Hook Form by calling the register function with the field name from each input element (e.g. {...register('title')}
).
import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import * as Yup from 'yup';
export default Home;
function Home() {
// form validation rules
const validationSchema = Yup.object().shape({
title: Yup.string()
.required('Title is required'),
firstName: Yup.string()
.required('First Name is required'),
lastName: Yup.string()
.required('Last name is required'),
dob: Yup.string()
.required('Date of Birth is required')
.matches(/^\d{4}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])$/, 'Date of Birth must be a valid date in the format YYYY-MM-DD'),
email: Yup.string()
.required('Email is required')
.email('Email is invalid'),
password: Yup.string()
.min(6, 'Password must be at least 6 characters')
.required('Password is required'),
confirmPassword: Yup.string()
.oneOf([Yup.ref('password'), null], 'Passwords must match')
.required('Confirm Password is required'),
acceptTerms: Yup.bool()
.oneOf([true], 'Accept Ts & Cs is required')
});
const formOptions = { resolver: yupResolver(validationSchema) };
// get functions to build form with useForm() hook
const { register, handleSubmit, reset, formState } = useForm(formOptions);
const { errors } = formState;
function onSubmit(data) {
// display form data on success
alert('SUCCESS!! :-)\n\n' + JSON.stringify(data, null, 4));
return false;
}
return (
<div className="card m-3">
<h5 className="card-header">Next.js - Form Validation Example</h5>
<div className="card-body">
<form onSubmit={handleSubmit(onSubmit)}>
<div className="form-row">
<div className="form-group col">
<label>Title</label>
<select name="title" {...register('title')} className={`form-control ${errors.title ? 'is-invalid' : ''}`}>
<option value=""></option>
<option value="Mr">Mr</option>
<option value="Mrs">Mrs</option>
<option value="Miss">Miss</option>
<option value="Ms">Ms</option>
</select>
<div className="invalid-feedback">{errors.title?.message}</div>
</div>
<div className="form-group col-5">
<label>First Name</label>
<input name="firstName" type="text" {...register('firstName')} className={`form-control ${errors.firstName ? 'is-invalid' : ''}`} />
<div className="invalid-feedback">{errors.firstName?.message}</div>
</div>
<div className="form-group col-5">
<label>Last Name</label>
<input name="lastName" type="text" {...register('lastName')} className={`form-control ${errors.lastName ? 'is-invalid' : ''}`} />
<div className="invalid-feedback">{errors.lastName?.message}</div>
</div>
</div>
<div className="form-row">
<div className="form-group col">
<label>Date of Birth</label>
<input name="dob" type="date" {...register('dob')} className={`form-control ${errors.dob ? 'is-invalid' : ''}`} />
<div className="invalid-feedback">{errors.dob?.message}</div>
</div>
<div className="form-group col">
<label>Email</label>
<input name="email" type="text" {...register('email')} className={`form-control ${errors.email ? 'is-invalid' : ''}`} />
<div className="invalid-feedback">{errors.email?.message}</div>
</div>
</div>
<div className="form-row">
<div className="form-group col">
<label>Password</label>
<input name="password" type="password" {...register('password')} className={`form-control ${errors.password ? 'is-invalid' : ''}`} />
<div className="invalid-feedback">{errors.password?.message}</div>
</div>
<div className="form-group col">
<label>Confirm Password</label>
<input name="confirmPassword" type="password" {...register('confirmPassword')} className={`form-control ${errors.confirmPassword ? 'is-invalid' : ''}`} />
<div className="invalid-feedback">{errors.confirmPassword?.message}</div>
</div>
</div>
<div className="form-group form-check">
<input name="acceptTerms" type="checkbox" {...register('acceptTerms')} id="acceptTerms" className={`form-check-input ${errors.acceptTerms ? 'is-invalid' : ''}`} />
<label htmlFor="acceptTerms" className="form-check-label">Accept Terms & Conditions</label>
<div className="invalid-feedback">{errors.acceptTerms?.message}</div>
</div>
<div className="form-group">
<button type="submit" className="btn btn-primary mr-1">Register</button>
<button type="button" onClick={() => reset()} className="btn btn-secondary">Reset</button>
</div>
</form>
</div>
</div>
);
}
Need Some NextJS Help?
Search fiverr for freelance NextJS developers.
Follow me for updates
When I'm not coding...
Me and Tina are on a motorcycle adventure around Australia.
Come along for the ride!