Stop Using Forms Like It's 2015: Master Complex Forms in React with Formik + Yup + Dynamic Fields
If you've ever built a form in React, chances are you've ended up tangled in useState() hooks, onChange handlers, and spaghetti validation logic that makes your app feel more like a bomb squad operation than front-end development.
❗ Let's face it: Forms in React suck... unless you're using the right tools.
Say hello to your new best friends:
In this blog post, you're going to level-up from basic form handling to building complex, conditional, dynamic, and validated forms like a true pro.
First, install the necessary packages:
npm install formik yup
import React from 'react';
import { Formik, Form, Field, ErrorMessage } from 'formik';
import * as Yup from 'yup';
const SignupSchema = Yup.object().shape({
name: Yup.string().required('Required'),
email: Yup.string().email('Invalid email').required('Required'),
password: Yup.string().min(6, 'Too Short!').required('Required')
});
const SimpleForm = () => (
<div>
<h1>Signup</h1>
<Formik
initialValues={{ name: '', email: '', password: '' }}
validationSchema={SignupSchema}
onSubmit={values => {
console.log(values);
}}
>
{() => (
<Form>
<label>Name</label>
<Field name="name" />
<ErrorMessage name="name" component="div" />
<label>Email</label>
<Field name="email" type="email" />
<ErrorMessage name="email" component="div" />
<label>Password</label>
<Field name="password" type="password" />
<ErrorMessage name="password" component="div" />
<button type="submit">Submit</button>
</Form>
)}
</Formik>
</div>
);
export default SimpleForm;
Let’s say you want users to input multiple hobbies, dynamically adding fields. You don’t want to guess how many fields they need, right? You want something smart.
Enter FieldArray ⛹️♂️
import { FieldArray } from 'formik';
const DynamicForm = () => (
<Formik
initialValues={{ hobbies: [''] }}
onSubmit={values => {
console.log(values);
}}
>
{({ values }) => (
<Form>
<h1>Enter Hobbies</h1>
<FieldArray name="hobbies">
{({ push, remove }) => (
<div>
{values.hobbies.map((_, index) => (
<div key={index}>
<Field name={`hobbies.${index}`} />
<button type="button" onClick={() => remove(index)}>
Remove
</button>
</div>
))}
<button type="button" onClick={() => push('')}>
Add Hobby
</button>
</div>
)}
</FieldArray>
<button type="submit">Submit</button>
</Form>
)}
</Formik>
);
Serious forms require conditional logic. For example, if a user selects "Employed", show employer-related fields.
const ConditionalForm = () => (
<Formik
initialValues={{ status: '', company: '' }}
validationSchema={Yup.object({
status: Yup.string().required(),
company: Yup.string().when('status', {
is: 'employed',
then: Yup.string().required('Company name is required')
})
})}
onSubmit={values => console.log(values)}
>
{({ values }) => (
<Form>
<label>Status</label>
<Field as="select" name="status">
<option value="">Select</option>
<option value="student">Student</option>
<option value="employed">Employed</option>
</Field>
<ErrorMessage name="status" component="div" />
{values.status === 'employed' && (
<>
<label>Company</label>
<Field name="company" />
<ErrorMessage name="company" component="div" />
</>
)}
<button type="submit">Submit</button>
</Form>
)}
</Formik>
);
✅ Now you’ve got schema-based validation AND dynamic fields — like a boss.
Don’t repeat yourself. Abstract form inputs like this:
const MyTextInput = ({ label, ...props }) => (
<div>
<label>{label}</label>
<Field {...props} />
<ErrorMessage name={props.name} component="div" />
</div>
);
// Use it like:
<MyTextInput label="Name" name="name" type="text" />
Formik forms return predictable props that are very testable using Testing Library:
import { render, screen, fireEvent } from '@testing-library/react';
import SimpleForm from './SimpleForm';
test('form submits correct data', () => {
render(<SimpleForm />);
fireEvent.change(screen.getByLabelText(/name/i), { target: { value: 'Alice' } });
fireEvent.change(screen.getByLabelText(/email/i), { target: { value: '[email protected]' } });
fireEvent.change(screen.getByLabelText(/password/i), { target: { value: '123456' } });
fireEvent.click(screen.getByText(/submit/i));
// Add your expectations or mock submit handler here
});
Formik + Yup isn’t just a pairing, it’s a superpower combo for form management in React. You’ve seen how to:
You’re now ready to build enterprise-grade forms without losing hair. 🧙♂️✨
Want to go further? Try integrating:
Or... try building a form builder using Formik + JSON schemas 🤯
Start building forms that work for users — not against them.
💡 If you need help building dynamic, interactive frontends like this - we offer frontend development services.
Information