Dynamic forms are forms where the fields can be added or removed dynamically based on user interaction or application logic. Handling dynamic forms efficiently in React involves managing state updates and rendering the correct number of fields based on the application’s requirements.
In this guide, we’ll explore how to create and manage dynamic forms in React, including adding/removing fields, handling form submission, and using libraries like Formik and React Hook Form to streamline the process.
1. Creating a Dynamic Form with React State
Let’s start with the basic approach using React’s useState
hook to manage form state dynamically. The idea is to allow users to add or remove input fields, and React will update the state accordingly.
Basic Example: Adding/Removing Input Fields
import React, { useState } from 'react';
const DynamicForm = () => {
const [fields, setFields] = useState([{ value: '' }]);
const handleChange = (index, event) => {
const values = [...fields];
values[index].value = event.target.value;
setFields(values);
};
const handleAddField = () => {
setFields([...fields, { value: '' }]);
};
const handleRemoveField = (index) => {
const values = [...fields];
values.splice(index, 1);
setFields(values);
};
const handleSubmit = (event) => {
event.preventDefault();
console.log('Form submitted with values:', fields);
};
return (
<form onSubmit={handleSubmit}>
{fields.map((field, index) => (
<div key={index} style={{ marginBottom: '10px' }}>
<input
type="text"
value={field.value}
onChange={(e) => handleChange(index, e)}
placeholder={`Field ${index + 1}`}
/>
<button type="button" onClick={() => handleRemoveField(index)}>Remove</button>
</div>
))}
<button type="button" onClick={handleAddField}>Add Field</button>
<button type="submit">Submit</button>
</form>
);
};
export default DynamicForm;
Explanation:
- State: The
fields
state holds an array of input field objects. Each field object has avalue
property that holds the current value of the field. - Adding Fields: The
handleAddField
function appends a new object to thefields
array, representing a new input field. - Removing Fields: The
handleRemoveField
function removes the field at the specified index. - Handling Changes: The
handleChange
function updates the value of a specific input field based on its index.
2. Dynamic Forms with Formik
Formik simplifies the management of form state, validation, and submission. For dynamic forms, Formik provides a FieldArray
component, which is useful when you need to manage an array of fields (such as multiple email addresses or phone numbers).
Example: Dynamic Fields with Formik and FieldArray
import React from 'react';
import { Formik, Form, Field, FieldArray } from 'formik';
import * as Yup from 'yup';
const validationSchema = Yup.object({
emails: Yup.array().of(
Yup.string().email('Invalid email address').required('Email is required')
),
});
const DynamicFormikForm = () => {
return (
<Formik
initialValues={{ emails: [''] }}
validationSchema={validationSchema}
onSubmit={(values) => {
console.log('Form submitted with values:', values);
}}
>
{({ values }) => (
<Form>
<FieldArray name="emails">
{({ push, remove }) => (
<div>
{values.emails.map((email, index) => (
<div key={index} style={{ marginBottom: '10px' }}>
<Field name={`emails[${index}]`} placeholder="Enter email" />
<button type="button" onClick={() => remove(index)}>
Remove
</button>
</div>
))}
<button type="button" onClick={() => push('')}>
Add Email
</button>
</div>
)}
</FieldArray>
<button type="submit">Submit</button>
</Form>
)}
</Formik>
);
};
export default DynamicFormikForm;
Explanation:
- FieldArray: Formik’s
FieldArray
component is used to handle dynamic form fields. Thepush
function adds a new field, and theremove
function removes a field. - Validation: Yup is used to validate each email field.
- Rendering Dynamic Fields: Each email input is rendered based on the length of the
emails
array in the Formik state.
3. Dynamic Forms with React Hook Form
React Hook Form is a lightweight alternative to Formik, known for its performance and simplicity. You can handle dynamic fields with React Hook Form using the useFieldArray
hook.
Example: Dynamic Fields with React Hook Form
import React from 'react';
import { useForm, useFieldArray, Controller } from 'react-hook-form';
const DynamicRHFForm = () => {
const { control, handleSubmit } = useForm({
defaultValues: {
emails: [''],
},
});
const { fields, append, remove } = useFieldArray({
control,
name: 'emails',
});
const onSubmit = (data) => {
console.log('Form submitted with values:', data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
{fields.map((item, index) => (
<div key={item.id} style={{ marginBottom: '10px' }}>
<Controller
name={`emails[${index}]`}
control={control}
render={({ field }) => <input {...field} placeholder="Enter email" />}
/>
<button type="button" onClick={() => remove(index)}>Remove</button>
</div>
))}
<button type="button" onClick={() => append('')}>Add Email</button>
<button type="submit">Submit</button>
</form>
);
};
export default DynamicRHFForm;
Explanation:
- useFieldArray: The
useFieldArray
hook provides a way to manage dynamic fields.append
adds a new field to the array, andremove
deletes an existing field. - Controller: React Hook Form’s
Controller
is used to wrap the input field and register it with the form state. - Rendering Dynamic Fields: The form dynamically renders inputs based on the
fields
array returned by theuseFieldArray
hook.
4. Key Considerations for Dynamic Forms
- Performance: Both Formik and React Hook Form handle dynamic fields efficiently. React Hook Form generally has better performance, especially for forms with many dynamic fields, due to its uncontrolled component model.
- Validation: With dynamic forms, validation becomes a little more complex, especially if you need to validate fields added at runtime. Both Formik (with Yup) and React Hook Form support dynamic validation well.
- Complexity: For simpler use cases or when you need fine control over performance, React Hook Form is a great choice. For more complex forms with heavy validation requirements, Formik provides a more feature-rich API with better out-of-the-box handling for error states and validation.