Creating a Dynamic Form with React Hook Form: A Step-by-Step Guide

Yaseer TasleemYaseer Tasleem
6 min read

Introduction

In the realm of modern web development, crafting interactive forms is a pivotal task. Whether it's for user registrations, data submissions, or any other input-driven interaction, forms play a pivotal role in user interfaces. Streamlining this process, the react-hook-form library offers a powerful and developer-friendly solution. This comprehensive tutorial will walk you through the process of creating a dynamic address form utilizing the useForm and useFieldArray hooks, generously provided by react-hook-form. By the end of this guide, you'll have gained a deep understanding of building adaptable forms that seamlessly integrate with the react-hook-form library.

Unraveling react-hook-form

Before we embark on creating our dynamic address form, let's take a moment to acquaint ourselves with the essence of the react-hook-form library. Renowned within the React developer community, this library stands as a favored solution for form management due to its emphasis on simplicity, optimal performance, and seamless integration with React's functional component model. By capitalizing on React's hooks, developers can effortlessly manage form states, validation, and submission logic through intuitive hooks-based functions. Leveraging uncontrolled components, react-hook-form orchestrates minimal re-renders, resulting in a smooth and efficient form-building experience.

Preparing the Ground

Before delving into the nitty-gritty, ensure that your React application is up and running. If you haven't already incorporated the react-hook-form library, initiate the process by executing the following command:

npm install react-hook-form

Constructing the Dynamic Address Form

Let's embark on the journey of creating our dynamic address form. To set the stage, we'll begin by importing the essential functions from the react-hook-form library. Subsequently, we'll lay the foundation by structuring the initial form setup.

import { useFieldArray, useForm } from 'react-hook-form';

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

  const { fields, append } = useFieldArray({
    control,
    name: 'addresses',
  });

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

  // Additional component code...
};

The code snippet above kicks off the process by importing the pivotal useForm and useFieldArray hooks from the react-hook-form library. The useForm hook empowers us to manage the form's state, while the useFieldArray hook aids us in managing dynamic arrays of form fields.

Crafting the Form User Interface

Now, let's transition to creating the user interface for our dynamic address form using HTML and the hooks graciously bestowed upon us by the react-hook-form library.

return (
  <div className='flex items-center justify-center min-h-screen bg-gray-100'>
    <form
      className='w-[600px] p-8 bg-white rounded-lg shadow-lg'
      onSubmit={handleSubmit(onSubmit)}
    >
      <h1 className='text-3xl text-center font-semibold text-gray-900 mb-6'>
        Address Form
      </h1>
      <div className='mb-6 p-6 bg-gray-50 rounded-md shadow-md'>
        {/* Personal information fields */}
      </div>
      {fields.map((field, index) => (
        <div className='mb-6 p-6 bg-gray-50 rounded-md shadow-md' key={field.id}>
          {/* Dynamic address fields */}
        </div>
      ))}
      <div className='flex items-center justify-end mt-6'>
        <button
          className='bg-blue-500 px-6 py-2 rounded-md text-white hover:bg-blue-600 transition duration-300'
          onClick={() => append({ street: '', city: '', zip: '' })}
          type='button'
        >
          Add Address
        </button>
        <button
          className='bg-blue-500 ml-4 px-6 py-2 rounded-md text-white hover:bg-blue-600 transition duration-300'
          type='submit'
        >
          Submit
        </button>
      </div>
    </form>
  </div>
);

The code segment above structures the form's visual representation:

  1. The form begins with a div element encompassing the entire form, offering a central alignment.

  2. The form tag carries essential attributes, such as its dimensions and submission handling via the handleSubmit function.

  3. A captivating h1 tag announces the purpose of the form.

  4. Div elements are employed for personal information fields and dynamic address sections.

  5. The fields.map function dynamically maps the address fields using the fields array.

  6. A final flex container accommodates the "Add Address" and "Submit" buttons, offering responsive and engaging interactions.

Managing Dynamic Addresses

Let's delve deeper into the art of handling dynamic address fields with the useFieldArray hook.

{fields.map((field, index) => (
  <div className='mb-6 p-6 bg-gray-50 rounded-md shadow-md' key={field.id}>
    <div className='flex flex-col mb-4'>
      <label htmlFor={`addresses.${index}.street`} className='text-gray-700 mb-2'>
        Street
      </label>
      <input
        type='text'
        className='border border-gray-300 rounded-md px-4 py-2 focus:outline-none focus:border-blue-500 transition duration-300'
        placeholder='Enter Street'
        name={`addresses.${index}.street`}
        {...register(`addresses.${index}.street`)}
      />
    </div>
    {/* City */}
    {/* Zip Code */}
  </div>
))}

Within this code excerpt, the enchantment of dynamically rendering address fields comes alive through the fields.map function. Each iteration manifests a div element that hosts inputs for the street, city, and zip code. The index parameter undertakes the vital role of ensuring that each field possesses a unique identity within the addresses array.

Infusing Dynamism: Adding Addresses On-the-Fly

To bestow users with the ability to seamlessly add new addresses, let's enrich our form with the captivating "Add Address" button.

<button
  className='bg-blue-500 px-6 py-2 rounded-md text-white hover:bg-blue-600 transition duration-300'
  onClick={() => append({ street: '', city: '', zip: '' })}
  type='button'
>
  Add Address
</button>

Upon engaging with the "Add Address" button, the magic of the append function materializes. This function introduces a fresh address object into the addresses array, permitting users to effortlessly augment the form with additional address fields.

Unveiling the Full Example

Encompassing all the facets of our dynamic address form is the following comprehensive example:

import { useFieldArray, useForm } from 'react-hook-form';

const App = () => {
    const { register, handleSubmit, control } = useForm({
        defaultValues: {
            firstName: '',
            lastName: '',
            email: '',
            addresses: [
                {
                    street: '',
                    city: '',
                    zip: '',
                },
            ],
        },
    });

    const { fields, append } = useFieldArray({
        control,
        name: 'addresses',
    });

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

    return (
        <div className='flex items-center justify-center min-h-screen bg-gray-100'>
            <form
                className='w-[600px] p-8 bg-white rounded-lg shadow-lg'
                onSubmit={handleSubmit(onSubmit)}
            >
                <h1 className='text-3xl text-center font-semibold text-gray-900 mb-6'>Address Form</h1>
                <div className='mb-6 p-6 bg-gray-50 rounded-md shadow-md'>
                    <div className='flex flex-col mb-4'>
                        <label htmlFor='firstName' className='text-gray-700 mb-2'>
                            First Name
                        </label>
                        <input
                            type='text'
                            className='border border-gray-300 rounded-md px-4 py-2 focus:outline-none focus:border-blue-500 transition duration-300'
                            name='firstName'
                            placeholder='Enter First Name'
                            {...register('firstName')}
                        />
                    </div>
                    <div className='flex flex-col mb-4'>
                        <label htmlFor='lastName' className='text-gray-700 mb-2'>
                            Last Name
                        </label>
                        <input
                            type='text'
                            className='border border-gray-300 rounded-md px-4 py-2 focus:outline-none focus:border-blue-500 transition duration-300'
                            name='lastName'
                            {...register('lastName')}
                            placeholder='Enter     Last Name'
                        />
                    </div>
                    <div className='flex flex-col mb-4'>
                        <label htmlFor='email' className='text-gray-700 mb-2'>
                            Email
                        </label>
                        <input
                            type='email'
                            className='border border-gray-300 rounded-md px-4 py-2 focus:outline-none focus:border-blue-500 transition duration-300'
                            name='email'
                            placeholder='Enter Email'
                            {...register('email')}
                        />
                    </div>
                </div>
                {fields.map((field, index) => (
                    <div className='mb-6 p-6 bg-gray-50 rounded-md shadow-md' key={field.id}>
                        <div className='flex flex-col mb-4'>
                            <label htmlFor={`addresses.${index}.street`} className='text-gray-700 mb-2'>
                                Street
                            </label>
                            <input
                                type='text'
                                className='border border-gray-300 rounded-md px-4 py-2 focus:outline-none focus:border-blue-500 transition duration-300'
                                placeholder='Enter Street'
                                name={`addresses.${index}.street`}
                                {...register(`addresses.${index}.street`)}
                            />
                        </div>
                        <div className='flex flex-col mb-4'>
                            <label htmlFor={`addresses.${index}.city`} className='text-gray-700 mb-2'>
                                City
                            </label>
                            <input
                                type='text'
                                className='border border-gray-300 rounded-md px-4 py-2 focus:outline-none focus:border-blue-500 transition duration-300'
                                placeholder='Enter City'
                                name={`addresses.${index}.city`}
                                {...register(`addresses.${index}.city`)}
                            />
                        </div>
                        <div className='flex flex-col'>
                            <label htmlFor={`addresses.${index}.zip`} className='text-gray-700 mb-2'>
                                Zip Code
                            </label>
                            <input
                                type='text'
                                className='border border-gray-300 rounded-md px-4 py-2 focus:outline-none focus:border-blue-500 transition duration-300'
                                placeholder='Enter Zip Code'
                                name={`addresses.${index}.zip`}
                                {...register(`addresses.${index}.zip`)}
                            />
                        </div>
                    </div>
                ))}
                <div className='flex items-center justify-end mt-6'>
                    <button
                        className='bg-blue-500 px-6 py-2 rounded-md text-white hover:bg-blue-600 transition duration-300'
                        onClick={() => append({ street: '', city: '', zip: '' })}
                        type='button'
                    >
                        Add Address
                    </button>
                    <button
                        className='bg-blue-500 ml-4 px-6 py-2 rounded-md text-white hover:bg-blue-600 transition duration-300'
                        type='submit'
                    >
                        Submit
                    </button>
                </div>
            </form>
        </div>
    );
};

export default App;

Key Insights

  • react-hook-form delivers a streamlined approach to form management within React applications.

  • The useForm and useFieldArray hooks constitute the foundation of form management and dynamic field handling.

  • Dynamic fields seamlessly unfold through the use of the fields array.

  • The append function introduces a new layer of dynamism by enabling users to effortlessly add new address fields.

Wrapping Up

In this comprehensive tutorial, we've embarked on a journey of crafting a dynamic address form utilizing the react-hook-form library. We've covered setting up the form, crafting the user interface, deftly managing dynamic fields, and empowering users to enhance their experience by dynamically adding new addresses. By following this guide, you've gained a comprehensive understanding of creating interactive forms that flawlessly adapt to user interactions. With its user-friendly approach and performance optimization, react-hook-form stands as a steadfast ally in your quest to develop seamless and user-centric forms.

1
Subscribe to my newsletter

Read articles from Yaseer Tasleem directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Yaseer Tasleem
Yaseer Tasleem

Front-End Developer