DEV Community

Cover image for πŸ”₯ Stop Using Forms Like It's 2015: Master Complex Forms in React with Formik + Yup + Dynamic Fields πŸ”₯
Yevhen Kozachenko πŸ‡ΊπŸ‡¦
Yevhen Kozachenko πŸ‡ΊπŸ‡¦

Posted on • Originally published at ekwoster.dev

πŸ”₯ Stop Using Forms Like It's 2015: Master Complex Forms in React with Formik + Yup + Dynamic Fields πŸ”₯

πŸš€ 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:

  • βœ… Formik β€” Dead-simple form state management.
  • βœ… Yup β€” Robust, schema-based validations.
  • βœ… Dynamic Fields β€” Because your forms aren't static, and your logic shouldn't be either.

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.


πŸ“¦ Setting the Stage: Basic Setup

First, install the necessary packages:

npm install formik yup
Enter fullscreen mode Exit fullscreen mode

Create a Simple Form

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;
Enter fullscreen mode Exit fullscreen mode

πŸ’‘ Going Dynamic: Adding Fields on the Fly

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 ⛹️‍♂️

Example: Add/Remove Dynamic Fields

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>
);
Enter fullscreen mode Exit fullscreen mode

🧠 Smart Forms: Conditional Rendering

Serious forms require conditional logic. For example, if a user selects "Employed", show employer-related fields.

Example: Employment Conditional 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>
);
Enter fullscreen mode Exit fullscreen mode

βœ… Now you’ve got schema-based validation AND dynamic fields β€” like a boss.


πŸ›  Bonus: Reusable Input Components

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" />
Enter fullscreen mode Exit fullscreen mode

πŸ§ͺ Testing Your Forms

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: 'alice@example.com' } });
  fireEvent.change(screen.getByLabelText(/password/i), { target: { value: '123456' } });

  fireEvent.click(screen.getByText(/submit/i));

  // Add your expectations or mock submit handler here
});
Enter fullscreen mode Exit fullscreen mode

🀯 Final Thoughts

Formik + Yup isn’t just a pairing, it’s a superpower combo for form management in React. You’ve seen how to:

  • Manage small or complex forms effortlessly
  • Add/remove fields dynamically
  • Validate fields conditionally
  • Create comprehensive reusable components

You’re now ready to build enterprise-grade forms without losing hair. πŸ§™β€β™‚οΈβœ¨


πŸš€ Next-Level Challenge

Want to go further? Try integrating:

  • Autosave feature on field change.
  • Async validation (e.g., check email availability).
  • Multi-step wizards with Formik context.

Or... try building a form builder using Formik + JSON schemas 🀯


🧡 TL;DR:

  • Use Formik for sanity 🎯
  • Use Yup for clarity πŸ”
  • Go dynamic for reality πŸŒ€

Start building forms that work for users β€” not against them.


Happy Coding! πŸš€

πŸ’‘ If you need help building dynamic, interactive frontends like this - we offer frontend development services.

Top comments (0)