React Hook Form Tutorial - Numbers, Dates, Checkboxes
/ 3 min read
In the previous post we have learned how to validate text inputs in our form.
In this post we’re going to learn:
- How to validate numbers and dates in
<input >
fields. - How to handle checkbox inputs.
Form requirements
We’re going to build a hotel booking form that has these requirements:
- Number of guests: number between 1 to 5.
- Check in date: date starting from tomorrow.
- Extra services that are optional for users to choose.

Define the validation schema
We define the schema fields and validations, and also convert them to the types we want instead of just using strings.
const tomorrow = new Date();tomorrow.setDate(tomorrow.getDate() + 1);tomorrow.setHours(0);tomorrow.setMinutes(0);tomorrow.setSeconds(0);
export const formSchema = z.object({ guests: z .number() .int("Must be a whole number") .min(1, "Must have at least 1 guest or more") .max(5, "Cannot have more than 5 guests"),
checkIn: z.date().min(tomorrow, "Can only book starting from tomorrow"),
breakfast: z.boolean(), wifi: z.boolean(), airportTransfer: z.boolean(),});
export type Form = z.TypeOf<typeof formSchema>;
Build the UI
Same drill with the previous tutorial, we’re going to create the UI components and handle inputs and submissions.
function App() { const { register, formState: { errors }, handleSubmit, } = useForm<Form>({ resolver: zodResolver(formSchema), });
return ( <form onSubmit={handleSubmit((data) => { console.log(data); })} > <div> <label htmlFor="guests">Number of Guests</label> <input id="guests" {...register("guests")} type="number" /> <div className="error">{errors.guests?.message}</div> </div>
<div className="field"> <label htmlFor="check-in">Check-In Date</label> <input id="check-in" {...register("checkIn")} type="date" /> <div className="error">{errors.checkIn?.message}</div> </div>
<div className="field"> <label>Extra Services</label> <label className="checkbox"> <input type="checkbox" {...register("breakfast")} /> <span className="check-label">Breakfast</span> </label>
<label className="checkbox"> <input type="checkbox" {...register("wifi")} /> <span className="check-label">Wi-Fi</span> </label>
<label className="checkbox"> <input type="checkbox" {...register("airportTransfer")} /> <span className="check-label">Airport Transfer</span> </label> </div>
<button type="submit">Submit</button> </form> );}
export default App;
Notice that for the guests and check-ins, I use <input type="number" />
and <input type="date" />
, and I don’t do anything special for the checkboxes.
Now submit the form with valid number of guests and check-in date and see what happens.

We already provided the valid inputs but why did the validation fail?
One important thing we need to remember about <input />
s behave: they return strings most of the time.
The type=number
and type=date
props that we use only change the appearance of the text field.
Making dates and numbers work
In order to fix this, we can convert the string inputs to the desired types using type coercion in Zod.
export const formSchema = z.object({ guests: z .number({ coerce: true }) .int("Must be a whole number") .min(1, "Must have at least 1 guest or more") .max(5, "Cannot have more than 5 guests"),
checkIn: z .date({ coerce: true, }) .min(tomorrow, "Can only book starting from tomorrow"),
breakfast: z.boolean(), wifi: z.boolean(), airportTransfer: z.boolean(),});
Now they should work just fine.
Object { guests: 2, checkIn: Date Thu Feb 29 2024 07:00:00 GMT+0700 (Western Indonesia Time), breakfast: false, wifi: true, airportTransfer: false}
Checkbox handlings are the opposite of numbers and dates, they just work.
Refining error messages
If you submit the form with blank fields you’ll get error messages like below.

The guest number field shows the message like what we defined.
As for the date error message we can modify by doing a minor modification in the schema.
export const formSchema = z.object({ guests: z .number({ coerce: true }) .int("Must be a whole number") .min(1, "Must have at least 1 guest or more") .max(5, "Cannot have more than 5 guests"),
checkIn: z .date({ coerce: true, errorMap: () => ({ message: "Please provide a valid check-in date", }), }) .min(tomorrow, "Can only book starting from tomorrow"),
breakfast: z.boolean(), wifi: z.boolean(), airportTransfer: z.boolean(),});

Try it yourself
I’ve summarized everything in this tutorial into the sandbox.