Why Your React Forms Suck — And How Formik Can Save Your Sanity
If you've ever built a form in React, chances are you've:
Sound familiar? Don't worry. You're not alone.
React doesn’t come with a built-in opinionated way to handle forms, and that’s both a blessing and a curse. While it gives you flexibility, it also leaves you reinventing the wheel for every form you build.
In this post, we’ll deep-dive into why form handling in React is often painful, and how the Formik library can drastically change your development experience.
Let’s consider a basic form with three fields — name, email, and password. Here’s what it might look like in vanilla React:
const [name, setName] = useState(''); const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [errors, setErrors] = useState({}); const handleSubmit = (e) => { e.preventDefault(); const newErrors = {}; if (!name) newErrors.name = "Name is required"; if (!email.includes("@")) newErrors.email = "Invalid email"; if (password.length < 6) newErrors.password = "Password too short"; setErrors(newErrors); if (Object.keys(newErrors).length === 0) { // Do something } };
Feels like a lot of code for something so small, right?
It only gets worse as the form scales.
Formik is a form library for React that handles form state, validation, errors, and more. Its philosophy: "Less Code. Fewer Bugs. Faster Development."
With Formik, you describe the shape of your form and its validation schema (optionally with Yup), and it manages the rest. Let's see the same form implemented with Formik.
import React from 'react'; import { Formik, Form, Field, ErrorMessage } from 'formik'; import * as Yup from 'yup'; const SignupForm = () => { const validationSchema = Yup.object({ name: Yup.string().required('Required'), email: Yup.string().email('Invalid email').required('Required'), password: Yup.string().min(6, 'Too short!').required('Required'), }); return ( <Formik initialValues={{ name: '', email: '', password: '' }} validationSchema={validationSchema} onSubmit={(values) => console.log(values)} > <Form> <div> <label>Name</label> <Field name="name" type="text" /> <ErrorMessage name="name" component="div" className="error" /> </div> <div> <label>Email</label> <Field name="email" type="email" /> <ErrorMessage name="email" component="div" className="error" /> </div> <div> <label>Password</label> <Field name="password" type="password" /> <ErrorMessage name="password" component="div" className="error" /> </div> <button type="submit">Submit</button> </Form> </Formik> ); }; export default SignupForm;
Suddenly, it’s all declarative, readable, and clean. Formik handles:
Formik offers some killer features:
Formik can also handle dynamic fields effortlessly. Need a list of hobbies where the user can add/remove inputs?
import { FieldArray } from 'formik'; <FieldArray name="hobbies"> {({ remove, push }) => ( <div> {values.hobbies.map((_, index) => ( <div key={index}> <Field name={`hobbies.${index}`} /> <button onClick={() => remove(index)}>-</button> </div> ))} <button onClick={() => push('')}>Add Hobby</button> </div> )} </FieldArray>
Formik isn’t perfect. Here are a few caveats:
In complex apps, you’ll likely need a multi-step form. Formik supports this pattern beautifully when paired with state management like React Context or even just clever use of higher-order components and validation schemas.
// Step 1: Personal Info // Step 2: Account Info // Step 3: Confirmation
Check out Formik’s GitHub Issue #811 for community-built solutions.
Formik is NOT just a form library. It’s a time-saving, mental-overhead-reducing, bug-killing machine. If you’ve been manually wiring up your forms, give your brain a break. Use Formik.
It saves you code, adds structure to your forms, and helps you ship faster.
Install Formik and start coding:
npm install formik yup
Trust us — your future self will thank you.
Interested in more dev painkillers like this? Follow for deep dives into tools that change the way we write code — for good.
Information