π₯ Stop Writing Boilerplate in React: Unlock the Hidden Power of Formik with Yup & Custom Hooks!
Tired of copying and pasting the same form validation logic again and again in your React projects? You're not alone. Forms are essential in web apps β login forms, registration forms, checkout forms, contact forms β the list goes on. But building and validating forms in React can quickly become a nightmare of state handlers, validations, and spaghetti code.
So let me show you a way out.
In this post, we're going to deep-dive into how Formik, combined with the Yup validation library and some custom hooks, can help you completely eliminate form boilerplate and elevate your developer experience (we're talking about never thinking about onChange again).
And this is not just another Formik tutorial. Weβll introduce an unconventional pattern using dynamic field rendering and reusable form components with context-driven data.
Ready? Letβs code like itβs 2030.
Weβll build a dynamic form component that:
Letβs be real β native form handling in React is a hassle. For a simple form with even basic validation, you might end up managing multiple state variables, a bunch of error conditions, and field-level logic that repeats across files.
Formik was built to reduce that pain. It abstracts away the form state and helps with validations, touched fields, error messages, and more.
Yup, on the other hand, is a schema validation library β think Joi, but more friendly and declarative for frontend forms.
When you merge these two tools with clever abstractions, magic happens.β¨
Imagine you have a form schema like this:
const formSchema = [ { name: 'email', type: 'text', label: 'Email', required: true }, { name: 'password', type: 'password', label: 'Password', required: true }, { name: 'acceptTerms', type: 'checkbox', label: 'Accept Terms', required: true } ];
This could come from a CMS, a config file, or your API. Now how about rendering an entire validated form based on this schema β in a completely reusable and abstracted way?
Letβs break this into 3 parts:
import * as Yup from 'yup'; const generateValidationSchema = (schema) => { const shape = {}; schema.forEach(field => { let validator; switch (field.type) { case 'text': case 'password': validator = Yup.string(); if (field.required) { validator = validator.required(`${field.label} is required`); } break; case 'checkbox': validator = Yup.bool(); if (field.required) { validator = validator.oneOf([true], `You must accept the terms`); } break; default: validator = Yup.mixed(); } shape[field.name] = validator; }); return Yup.object().shape(shape); };
Boom. This allows you to dynamically generate a Yup schema based on a simple config.
const DynamicField = ({ field, formik }) => { const { name, type, label } = field; switch (type) { case 'text': case 'password': return ( <div> <label>{label}</label> <input name={name} type={type} value={formik.values[name]} onChange={formik.handleChange} onBlur={formik.handleBlur} /> {formik.touched[name] && formik.errors[name] && ( <div className="error">{formik.errors[name]}</div> )} </div> ); case 'checkbox': return ( <div> <label> <input name={name} type="checkbox" checked={formik.values[name]} onChange={formik.handleChange} onBlur={formik.handleBlur} /> {label} </label> {formik.touched[name] && formik.errors[name] && ( <div className="error">{formik.errors[name]}</div> )} </div> ); default: return null; } };
Add styles and accessibility later β this is just the logic.
import { Formik, Form } from 'formik'; const DynamicForm = ({ schema }) => { const initialValues = schema.reduce((acc, field) => { acc[field.name] = field.type === 'checkbox' ? false : ''; return acc; }, {}); const validationSchema = generateValidationSchema(schema); const handleSubmit = (values) => { console.log('Form submitted:', values); }; return ( <Formik initialValues={initialValues} validationSchema={validationSchema} onSubmit={handleSubmit} > {(formik) => ( <Form> {schema.map(field => ( <DynamicField key={field.name} field={field} formik={formik} /> ))} <button type="submit">Submit</button> </Form> )} </Formik> ); };
Yes, that's it. You now have a generic component that works for arbitrary form configurations, with Yup validation fully integrated.
To add more field types, just extend the DynamicField component. Want a date picker? Autocomplete? Multiselect? Sky's the limit.
You can use this pattern for:
Once you empower your forms through configuration + Yup + Formik, everything becomes easier.
Formik on its own is powerful. But once you pair it with Yup and push abstraction, you can remove 90% of form-related boilerplate from your React project.
And if youβre just starting β even better. Youβre learning a modern solution that can grow with your projects and future-proof your code.
Go ahead, try implementing a fully dynamic form UI in your next app. Convert hardcoded forms to this system β you wonβt look back.
Let the code work for you. π―
Happy coding! π»
π‘ If you need help building modern frontends with highly reusable components, we offer Frontend Development services.
Information