Dynamic Forms and Validation Management in React using Formik and Yup
Learn how to create dynamic forms with robust validation in React using Formik and Yup. Discover advanced techniques for handling form state, conditional fields, and validation schemas in web applications.
Dynamic forms are a fundamental part of web applications, allowing users to input, manipulate, and submit data efficiently. In the React ecosystem, two powerful tools, Formik and Yup, stand out for creating dynamic forms and managing validation effortlessly. This article explores how to create advanced dynamic forms with robust validation using Formik and Yup.
What are Dynamic Forms?
Dynamic forms allow the structure or content of a form to change in response to user actions. For instance, a form might display different fields depending on user input or conditions, such as showing a new set of questions based on a previously selected answer. Dynamic forms improve user experience by only displaying relevant fields and helping streamline the data entry process.
Managing dynamic forms can be challenging, especially when it comes to maintaining state and ensuring correct validation of form fields. This is where libraries like Formik and Yup come into play.
Introducing Formik
Formik is a popular library in the React ecosystem for managing form state. It simplifies form creation by handling the complexities of form state management, validation, and form submission. Some of its core benefits include:
- State Management: Formik handles the state of form fields, ensuring that they are kept up-to-date.
- Field Value Tracking: It automatically tracks user inputs and updates the state in real-time.
- Validation: Formik integrates well with Yup for validation, providing a simple and scalable way to ensure form data correctness.
- Flexible Form Rendering: Formik provides a flexible API, making it easy to build dynamic forms that can adapt to changes in user input.
Basic Formik Example
Here’s a simple example of how to create a basic form using Formik:
import React from 'react';
import { Formik, Form, Field, ErrorMessage } from 'formik';
import * as Yup from 'yup';
const SignupForm = () => {
return (
<Formik
initialValues={{ email: '', password: '' }}
validationSchema={Yup.object({
email: Yup.string().email('Invalid email').required('Required'),
password: Yup.string().min(6, 'Password too short').required('Required')
})}
onSubmit={(values, { setSubmitting }) => {
setTimeout(() => {
console.log(JSON.stringify(values, null, 2));
setSubmitting(false);
}, 400);
}}
>
{formik => (
<Form>
<label htmlFor="email">Email</label>
<Field name="email" type="email" />
<ErrorMessage name="email" />
<label htmlFor="password">Password</label>
<Field name="password" type="password" />
<ErrorMessage name="password" />
<button type="submit" disabled={formik.isSubmitting}>Submit</button>
</Form>
)}
</Formik>
);
};
Key Concepts:
- Formik component: Provides the context and state management for the form.
- Field component: Handles rendering individual form fields and updating their state.
- ErrorMessage component: Displays validation messages.
initialValues
: Sets default values for the form fields.onSubmit
: Defines the function to be executed when the form is submitted.
Adding Yup for Validation
Formik by itself offers basic validation functionality, but for more advanced validation scenarios, Yup, a JavaScript schema validation library, is commonly used. Yup allows you to define validation schemas using a simple and declarative syntax.
Validating with Yup
In the above example, we use Yup for validating the form. Here’s a breakdown:
Yup.object({
email: Yup.string().email('Invalid email').required('Required'),
password: Yup.string().min(6, 'Password too short').required('Required')
})
Yup.string()
ensures that the field is treated as a string..email()
checks for valid email format..required()
makes the field mandatory..min()
checks that the password is at least 6 characters long.
Advanced Yup Validation
Yup allows for more complex validation rules as well, such as validating based on other fields, using regular expressions, or even creating custom validation rules.
Yup.object({
email: Yup.string().email('Invalid email').required('Required'),
password: Yup.string()
.min(6, 'Password too short')
.matches(/[a-zA-Z]/, 'Password can only contain Latin letters.')
.required('Required'),
confirmPassword: Yup.string()
.oneOf([Yup.ref('password'), null], 'Passwords must match')
.required('Required')
})
Dynamic Forms with Formik
Dynamic forms often require fields to appear or disappear based on user interaction. In Formik, this is easy to achieve by conditionally rendering fields based on form values.
Example: Conditional Fields
Let’s look at an example where we ask the user whether they want to sign up for a newsletter, and based on their response, we display an additional field for their email preferences.
const DynamicForm = () => {
return (
<Formik
initialValues={{ subscribe: false, email: '', frequency: '' }}
validationSchema={Yup.object({
email: Yup.string().email('Invalid email').when('subscribe', {
is: true,
then: Yup.string().required('Email is required'),
}),
frequency: Yup.string().when('subscribe', {
is: true,
then: Yup.string().required('Please select a frequency'),
}),
})}
onSubmit={(values, { setSubmitting }) => {
console.log(values);
setSubmitting(false);
}}
>
{({ values }) => (
<Form>
<label>
<Field type="checkbox" name="subscribe" />
Subscribe to newsletter
</label>
{values.subscribe && (
<>
<label htmlFor="email">Email</label>
<Field name="email" type="email" />
<ErrorMessage name="email" />
<label htmlFor="frequency">How often?</label>
<Field name="frequency" as="select">
<option value="">Select...</option>
<option value="daily">Daily</option>
<option value="weekly">Weekly</option>
</Field>
<ErrorMessage name="frequency" />
</>
)}
<button type="submit">Submit</button>
</Form>
)}
</Formik>
);
};
Key Takeaways:
when()
in Yup: Thewhen()
method allows for conditional validation based on other form values.- Dynamic Rendering: By using
values.subscribe
, we can conditionally render fields in the form based on whether the user opts in to the newsletter.
Managing Complex Dynamic Forms
When creating large, multi-step, or deeply dynamic forms, managing state and validation can become complex. Formik offers additional helpers to simplify these situations:
- Field Arrays: For cases where you need to dynamically add and remove multiple fields, like a list of phone numbers or addresses, Formik provides the
FieldArray
component. - Multi-step Forms: Formik’s flexible structure makes it ideal for building multi-step forms, where the state of each step can be managed separately and validated progressively.
Example: Field Array
import { FieldArray } from 'formik';
const PhoneNumbersForm = () => (
<Formik
initialValues={{ phoneNumbers: [''] }}
onSubmit={values => console.log(values)}
>
{formik => (
<Form>
<FieldArray name="phoneNumbers">
{({ insert, remove, push }) => (
<div>
{formik.values.phoneNumbers.map((phone, index) => (
<div key={index}>
<Field name={`phoneNumbers.${index}`} placeholder="Phone Number" />
<button type="button" onClick={() => remove(index)}>Remove</button>
</div>
))}
<button type="button" onClick={() => push('')}>Add Phone Number</button>
</div>
)}
</FieldArray>
<button type="submit">Submit</button>
</Form>
)}
</Formik>
);
Conclusion
Formik and Yup together provide a powerful solution for managing dynamic forms and validation in React applications. Formik simplifies form state management, while Yup allows for rich, flexible validation schemas. By leveraging the power of these two libraries, you can build complex, dynamic forms that provide a seamless user experience and ensure data correctness.
Whether you're dealing with simple forms or multi-step, conditional, dynamic forms, Formik and Yup are essential tools to have in your React toolkit.