Back

Conditionally Render Fields Using React Hook Form

Preston Kelly
Preston KellyTuesday, November 15, 2022
a car driving through a tunnel

How can you conditionally render fields using React Hook Form? Continue reading for a step by step guide on how I got there. I'll also highlight some common pitfalls you may encounter. To begin, I created a simple form with Tailwind styling. This form displays a first name input, submit button and a checkbox that checks to see if the user has a last name.

TL;DR

Use React Hook Form's built-in watch function to watch for changes to an input field. Next, use React’s useEffect hook to register and unregister the fields.

<form> <div className="flex flex-col"> <input placeholder="First Name" className="border-2" /> <div className="flex justify-between"> <label htmlFor="lastNameCheck">Last Name?</label> <input id="lastNameCheck" type="checkbox" /> </div> <input placeholder="Last Name" className="border-2" /> <button type="submit" className="bg-gray-500"> Submit </button> </div> </form>

When we're creating a new input, we'll want to register that field with React Hook Form using the register function. We'll spread the register function on the input and give it a name. This name will be something that we can reference later on when we're looking to conditionally remove the field.

Handle Form

Now, let’s register our fields and create a simple onSubmit function that will console log our form data. I’ve also included a User type that we’ll use to tell React Hook Form’s useForm function what type of data we’ll be including.

import { useForm } from "react-hook-form"; type User = { firstName: string; lastNameCheck: boolean; lastName: string; }; export const Form = () => { const { register, handleSubmit } = useForm<User>(); const onSubmit = (data: User) => console.log(data); return ( <> <form onSubmit={handleSubmit(onSubmit)}> <div className="flex flex-col"> <input {...register("firstName")} placeholder="First Name" className="border-2" /> <div className="flex justify-between"> <label htmlFor="lastNameCheck">Last Name?</label> <input id="lastNameCheck" type="checkbox" {...register("lastNameCheck")} /> </div> <input {...register("lastName")} placeholder="Last Name" className="border-2" /> <button type="submit" className="bg-gray-500"> Submit </button> </div> </form> </> ); };

Create Variable

Next, we'll create a variable using React Hook Form's built in watch function that will "watch" or keep track of field changes. This watch function give us updates along the way. For simplicity, we’ll name our variable watchLastNameCheck, and this variable will track any changes to our “lastNameCheck” checkbox.

const { register, handleSubmit, watch } = useForm<User>(); const watchLastNameCheck = watch("lastNameCheck");

If you enter “Hello” into the First Name input and click the submit button, you’ll see data for the first name and checkbox fields in the console.

If we were to click on the "Last Name?" checkbox, enter in “World” and click submit again, we'll see that our lastName field is now returning in the console.

Possible Pitfall

What happens if a user unchecks the checkbox? When a user unchecks the checkbox, we still see the lastName field outputting to the console and this is something we do not want! Returning empty or null fields can lead to errors down the road. We'll only want information from fields that we've entered data into and are visually rendered on the page.

The solution? We'll add in a useEffect to check the watchLastNameCheck variable we setup earlier. We'll register that input only when we need it. When a user unchecks our checkbox, we'll “unregister” the field using the built in unregister function from React Hook Form.

const { register, handleSubmit, unregister, watch } = useForm<User>(); const watchLastNameCheck = watch("lastNameCheck"); useEffect(() => { if (watchLastNameCheck) { register("lastName"); } else { unregister("lastName"); } }, [register, unregister, watchLastNameCheck]);

You'll see that when you now check and uncheck the checkbox, the console output only displays the information we need. The useEffect removes the lastName field from our form data if the checkbox is not checked. I’ve gone ahead and added a check to only display the lastName field when the checkbox is active.

Full code:

import { useEffect } from "react"; import { useForm } from "react-hook-form"; type User = { firstName: string; lastNameCheck: boolean; lastName: string; }; export const Form = () => { const { register, handleSubmit, unregister, watch } = useForm<User>(); const watchLastNameCheck = watch("lastNameCheck"); useEffect(() => { if (watchLastNameCheck) { register("lastName"); } else { unregister("lastName"); } }, [register, unregister, watchLastNameCheck]); const onSubmit = (data: User) => console.log(data); return ( <> <form onSubmit={handleSubmit(onSubmit)}> <div className="flex flex-col"> <input {...register("firstName")} placeholder="First Name" className="border-2" /> <div className="flex justify-between"> <label htmlFor="lastNameCheck">Last Name?</label> <input id="lastNameCheck" type="checkbox" {...register("lastNameCheck")} /> </div> {watchLastNameCheck ? ( <input {...register("lastName")} placeholder="Last Name" className="border-2" /> ) : null} <button type="submit" className="bg-gray-500"> Submit </button> </div> </form> </> ); };

Share this post

twitterfacebooklinkedin

Interested in working with us?

Give us some details about your project, and our team will be in touch with how we can help.

Get in Touch