Building Robust React Forms with Formik and Yup
Streamline validation and management for highly interactive forms
Aug 13, 2024 - 10:53 • 6 min read
Forms are a vital part of web applications, and building them with React can be challenging, especially when it comes to validation and managing user input. You might have found yourself stuck at some point when trying to create a form that suits your application's needs seamlessly. Fortunately, Formik and Yup together can help you resolve these issues effectively, enabling you to create robust forms that enhance user experience.
Why Formik?
Formik is a popular React library that simplifies handling complex forms in React applications. It provides an efficient way to manage form state, form submission, and lightweight validation without intricate boilerplate code. Formik’s API is tailored to work exceptionally well with React functional components, and it integrates seamlessly with built-in hooks.
Why Yup?
Yup is an object schema validator and object parser. It complements Formik beautifully as a validation library, allowing you to define a schema for your forms using a straightforward syntax. Yup makes it simple to validate various types of inputs, ensuring your forms are reliable and user-friendly.
Setting Up
Let's kick things off by setting up a new React project and installing Formik and Yup:
npx create-react-app formik-yup-example
cd formik-yup-example
npm install formik yup
Creating Your First Form with Formik
Now, let’s create a simple form using Formik. The form will include a text input and a submit button. We will validate the text input to ensure it’s not empty.
import React from 'react';
import { Formik, Form, Field, ErrorMessage } from 'formik';
const MyForm = () => {
return (
<Formik
initialValues={{ name: '' }}
validate={values => {
const errors = {};
if (!values.name) {
errors.name = 'Required';
}
return errors;
}}
onSubmit={(values, { setSubmitting }) => {
// Simulate a server request
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
setSubmitting(false);
}, 400);
}}
>
{({ isSubmitting }) => (
<Form>
<label htmlFor="name">Name:</label>
<Field type="text" name="name" />
<ErrorMessage name="name" component="div" />
<button type="submit" disabled={isSubmitting}>Submit</button>
</Form>
)}
</Formik>
);
};
export default MyForm;
In this component, we use Formik’s Formik
component to initialize our form. We specify the initial values through the initialValues
prop. Next, we implement a validate
function that checks for empty input fields, returning an error object if an error occurs. The onSubmit
callback handles form submission and can be modified to perform your logic, such as API calls or state management.
Integrating Yup for Validation
While Formik’s validation is nice for basic checks, validations can become cumbersome as forms get more complex. This is where Yup comes in handy. We can use Yup for validation schema instead of the inline validation function. Here’s how we will do it:
import * as Yup from 'yup';
const validationSchema = Yup.object().shape({
name: Yup.string().required('Name is required'),
});
const MyForm = () => {
return (
<Formik
initialValues={{ name: '' }}
validationSchema={validationSchema}
onSubmit={(values, { setSubmitting }) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
setSubmitting(false);
}, 400);
}}
>
{({ isSubmitting }) => (
<Form>
<label htmlFor="name">Name:</label>
<Field type="text" name="name" />
<ErrorMessage name="name" component="div" />
<button type="submit" disabled={isSubmitting}>Submit</button>
</Form>
)}
</Formik>
);
};
Using Yup, you define a validationSchema
that wraps your validation logic in a structured format. In this case, we defined a rule stating that the name field is both a string and required. Formik will now utilize this schema for validation when the form is submitted or when fields are updated.
Advanced Validation Scenarios
Yup enables us to create complex validation scenarios easily. Below, we will show how to set up compound validation rules, such as checking that a password is strong enough and confirming that it matches a second password field.
const validationSchema = Yup.object().shape({
password: Yup.string()
.min(6, 'Password must be at least 6 characters')
.required('Password is required'),
confirmPassword: Yup.string()
.oneOf([Yup.ref('password'), null], 'Passwords must match')
.required('Confirm Password is required'),
});
const MyForm = () => {
return (
<Formik
initialValues={{ password: '', confirmPassword: '' }}
validationSchema={validationSchema}
onSubmit={(values, { setSubmitting }) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
setSubmitting(false);
}, 400);
}}
>
{({ isSubmitting }) => (
<Form>
<label htmlFor="password">Password:</label>
<Field type="password" name="password" />
<ErrorMessage name="password" component="div" />
<label htmlFor="confirmPassword">Confirm Password:</label>
<Field type="password" name="confirmPassword" />
<ErrorMessage name="confirmPassword" component="div" />
<button type="submit" disabled={isSubmitting}>Submit</button>
</Form>
)}
</Formik>
);
};
Here, we have defined strong password rules. The password must be a minimum of six characters, and it must match the second password field. Utilizing Yup.ref
, we can create dynamic validation rules that refer to other fields in our form.
Handling Nested Forms
Sometimes, you may require nested forms for managing complex data structures. Formik handles this efficiently using the FieldArray
component, which helps managing an array of fields dynamically. Here’s how to use it:
import { FieldArray } from 'formik';
const MyNestedForm = () => {
return (
<Formik
initialValues={{ friends: [''] }}
onSubmit={(values, { setSubmitting }) => {
alert(JSON.stringify(values, null, 2));
setSubmitting(false);
}}
>
{({ values, isSubmitting }) => (
<Form>
<FieldArray name="friends">
{({ remove, push }) => (
<div>
{values.friends.length > 0 &&
values.friends.map((friend, index) => (
<div key={index}>
<Field name={`friends.${index}`} />
<button type="button" onClick={() => remove(index)}>Remove</button>
</div>
))
}
<button
type="button"
onClick={() => push('')}
>
Add Friend
</button>
</div>
)}
</FieldArray>
<button type="submit" disabled={isSubmitting}>Submit</button>
</Form>
)}
</Formik>
);
};
In this example, we are utilizing FieldArray
to manage a list of friends. Users can add or remove friends dynamically. Formik updates the form values automatically as users interact.
Accessibility Considerations
To ensure your forms are accessible to all users, consider following best practices:
- Ensure proper labeling using
htmlFor
andField
attributes. - Use screen reader-friendly messages for validation errors with
ErrorMessage
. - Give proper feedback on success or failure during form submissions.
Integrating Formik and Yup will streamline the process of building complex forms with React, providing an optimized and user-friendly experience. The methodologies shared not only enable sophisticated validations but also allow for form scalability as application requirements grow. As web applications continue to evolve, utilizing libraries like Formik and Yup will undoubtedly facilitate better user interactions through intuitive and efficient forms.
Conclusion
Mastering form management is key to creating fluid user experiences in web applications. Formik and Yup simplify and streamline this process, allowing you to dedicate more time to build features rather than managing forms. Through effective validation and management practices, you can ensure that your forms are not only functional but also enhance user engagement. Start integrating Formik and Yup into your React applications today, and observe how they enhance your forms with ease.