Building forms in React can quickly become complex, especially when dealing with multiple input fields, real-time validation, error messages, and submission logic. Managing form state, handling input changes, and validating user input manually can lead to verbose and error-prone code. This is where Formik comes to the rescue! Formik is a popular open-source library that simplifies the entire form-building process in React, making it more manageable, predictable, and enjoyable. In this post, we'll explore how to leverage Formik to create robust and user-friendly forms.
Why Use Formik?
Formik abstracts away much of the boilerplate code associated with forms, providing a streamlined API for:
- Getting values in and out of form state: No more manual `onChange` handlers for every input.
- Validation and error messages: Easily integrate validation schemas (like Yup) and display errors.
- Handling form submission: Simplifies the process of submitting data.
- Accessibility: Helps in building accessible forms by managing focus and states.
Key Formik Concepts
Before diving into code, let's understand some core components and hooks Formik provides:
- `Formik` Component: The main wrapper component that manages form state and submission.
- `useFormik` Hook: A React Hook that provides access to Formik's state and methods, ideal for functional components.
- `Field` Component: A helper component that automatically connects form inputs to Formik's state.
- `ErrorMessage` Component: Displays validation errors for a specific field.
- `Yup` (Optional but Recommended): A popular schema validation library that integrates seamlessly with Formik for robust validation.
Basic Setup and a Simple Form
First, you'll need to install Formik (and Yup if you plan to use it for validation):
npm install formik yup
Here's a basic login form using the `useFormik` hook:
import React from 'react';
import { useFormik } from 'formik';
const LoginForm = () => {
const formik = useFormik({
initialValues: {
email: '',
password: '',
},
onSubmit: values => {
alert(JSON.stringify(values, null, 2));
},
});
return (
<form onSubmit={formik.handleSubmit} class="space-y-4 p-6 bg-white rounded-lg shadow-md">
<div>
<label htmlFor="email" class="block text-sm font-medium text-gray-700">Email Address</label>
<input
id="email"
name="email"
type="email"
onChange={formik.handleChange}
value={formik.values.email}
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
/>
</div>
<div>
<label htmlFor="password" class="block text-sm font-medium text-gray-700">Password</label>
<input
id="password"
name="password"
type="password"
onChange={formik.handleChange}
value={formik.values.password}
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
/>
</div>
<button
type="submit"
class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
>
Submit
</button>
</form>
);
};
export default LoginForm;
Validation with Yup
Integrating Yup for schema-based validation is a common and powerful pattern with Formik.
import React from 'react';
import { useFormik } from 'formik';
import * as Yup from 'yup'; // Import Yup
const SignupForm = () => {
const formik = useFormik({
initialValues: {
firstName: '',
lastName: '',
email: '',
},
validationSchema: Yup.object({ // Define validation schema
firstName: Yup.string()
.max(15, 'Must be 15 characters or less')
.required('Required'),
lastName: Yup.string()
.max(20, 'Must be 20 characters or less')
.required('Required'),
email: Yup.string()
.email('Invalid email address')
.required('Required'),
}),
onSubmit: values => {
alert(JSON.stringify(values, null, 2));
},
});
return (
<form onSubmit={formik.handleSubmit} class="space-y-4 p-6 bg-white rounded-lg shadow-md">
<div>
<label htmlFor="firstName" class="block text-sm font-medium text-gray-700">First Name</label>
<input
id="firstName"
name="firstName"
type="text"
onChange={formik.handleChange}
onBlur={formik.handleBlur} // Add onBlur to trigger validation on blur
value={formik.values.firstName}
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
/>
{formik.touched.firstName && formik.errors.firstName ? (
<div class="mt-1 text-sm text-red-600">{formik.errors.firstName}</div>
) : null}
</div>
<div>
<label htmlFor="lastName" class="block text-sm font-medium text-gray-700">Last Name</label>
<input
id="lastName"
name="lastName"
type="text"
onChange={formik.handleChange}
onBlur={formik.handleBlur}
value={formik.values.lastName}
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
/>
{formik.touched.lastName && formik.errors.lastName ? (
<div class="mt-1 text-sm text-red-600">{formik.errors.lastName}</div>
) : null}
</div>
<div>
<label htmlFor="email" class="block text-sm font-medium text-gray-700">Email Address</label>
<input
id="email"
name="email"
type="email"
onChange={formik.handleChange}
onBlur={formik.handleBlur}
value={formik.values.email}
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
/>
{formik.touched.email && formik.errors.email ? (
<div class="mt-1 text-sm text-red-600">{formik.errors.email}</div>
) : null}
</div>
<button
type="submit"
class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
>
Submit
</button>
</form>
);
};
export default SignupForm;
Using `Field` and `ErrorMessage` Components
For even cleaner code, Formik provides `Field
` and `ErrorMessage
` components.
import React from 'react';
import { Formik, Form, Field, ErrorMessage } from 'formik'; // Import Formik, Form, Field, ErrorMessage
import * as Yup from 'yup';
const ContactForm = () => {
return (
<Formik
initialValues={{ name: '', email: '', message: '' }}
validationSchema={Yup.object({
name: Yup.string()
.max(50, 'Must be 50 characters or less')
.required('Required'),
email: Yup.string()
.email('Invalid email address')
.required('Required'),
message: Yup.string()
.min(10, 'Must be at least 10 characters')
.required('Required'),
})}
onSubmit={(values, { setSubmitting }) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
setSubmitting(false);
}, 400);
}}
>
{({ isSubmitting }) => (
<Form class="space-y-4 p-6 bg-white rounded-lg shadow-md">
<div>
<label htmlFor="name" class="block text-sm font-medium text-gray-700">Name</label>
<Field
name="name"
type="text"
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
/>
<ErrorMessage name="name" component="div" class="mt-1 text-sm text-red-600" />
</div>
<div>
<label htmlFor="email" class="block text-sm font-medium text-gray-700">Email</label>
<Field
name="email"
type="email"
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
/>
<ErrorMessage name="email" component="div" class="mt-1 text-sm text-red-600" />
</div>
<div>
<label htmlFor="message" class="block text-sm font-medium text-gray-700">Message</label>
<Field
name="message"
as="textarea" // Render as a textarea
rows="4"
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm resize-y"
/>
<ErrorMessage name="message" component="div" class="mt-1 text-sm text-red-600" />
</div>
<button
type="submit"
disabled={isSubmitting}
class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed"
>
{isSubmitting ? 'Submitting...' : 'Send Message'}
</button>
</Form>
)}
</Formik>
);
};
export default ContactForm;
Formik significantly streamlines the process of building and managing forms in React. By handling form state, validation, and submission boilerplate, it allows developers to focus on the core logic and UI of their applications. Coupled with a validation library like Yup, Formik provides a powerful and flexible solution for creating robust, accessible, and user-friendly forms with minimal effort. Start integrating Formik into your React projects today and experience the difference!