Editorâs note: This article was updated January 28 2022 to update any outdated information and add the Using Formikâs handleChange section, Using Formikâs onSubmit section, and Using Formikâs setSubmitting section.
Building forms with React involves setting up state as the container for user data and props as the means to control how state is updated using user input. Validation can be done in between user inputs, and an arbitrary submit function is executed on form submit.
In this article, weâll review how to use Formik in React to build forms, including:
handleChangeonSubmitisSubmittingsetSubmittingBefore we learn how to use Formik, letâs understand how forms are written in React without libraries and with minimal Bootstrap styling.
In the example below, we first initialize required state values in the constructor method. Because we have two required inputs â email and password â we initialize state for input values, input validity, and input errors:
constructor(props) {
super(props);
this.state = {
formValues: {
email: "",
password: ""
},
formErrors: {
email: "",
password: ""
},
formValidity: {
email: false,
password: false
},
isSubmitting: false
};
}
Next, we create the render the formâs method with input values derived from state:
render() {
const { formValues, formErrors, isSubmitting } = this.state;
return (
<div className="container">
<div className="row mb-5">
<div className="col-lg-12 text-center">
<h1 className="mt-5">Login Form</h1>
</div>
</div>
<div className="row">
<div className="col-lg-12">
<form onSubmit={this.handleSubmit}>
<div className="form-group">
<label>Email address</label>
<input
type="email"
name="email"
className={`form-control ${
formErrors.email ? "is-invalid" : ""
}`}
placeholder="Enter email"
onChange={this.handleChange}
value={formValues.email}
/>
<div className="invalid-feedback">{formErrors.email}</div>
</div>
<div className="form-group">
<label>Password</label>
<input
type="password"
name="password"
className={`form-control ${
formErrors.password ? "is-invalid" : ""
}`}
placeholder="Password"
onChange={this.handleChange}
value={formValues.password}
/>
<div className="invalid-feedback">{formErrors.password}</div>
</div>
<button
type="submit"
className="btn btn-primary btn-block"
disabled={isSubmitting}
>
{isSubmitting ? "Please wait..." : "Submit"}
</button>
</form>
</div>
</div>
</div>
);
}
Now we must write the handleChange method to update the state with user inputs:
handleChange = ({ target }) => {
const { formValues } = this.state;
formValues[target.name] = target.value;
this.setState({ formValues });
this.handleValidation(target);
};
Anytime the state values are updated, weâll run a validation method against user inputs. This is our handleValidation method:
handleValidation = target => {
const { name, value } = target;
const fieldValidationErrors = this.state.formErrors;
const validity = this.state.formValidity;
const isEmail = name === "email";
const isPassword = name === "password";
const emailTest = /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/i;
validity[name] = value.length > 0;
fieldValidationErrors[name] = validity[name]
? ""
: `${name} is required and cannot be empty`;
if (validity[name]) {
if (isEmail) {
validity[name] = emailTest.test(value);
fieldValidationErrors[name] = validity[name]
? ""
: `${name} should be a valid email address`;
}
if (isPassword) {
validity[name] = value.length >= 3;
fieldValidationErrors[name] = validity[name]
? ""
: `${name} should be 3 characters minimum`;
}
}
this.setState({
formErrors: fieldValidationErrors,
formValidity: validity
});
};
The last part of this basic form is a handleSubmit method for the submission process. We need to check on formValidity values, and, if there are any false values, run the validation method again without submitting the form:
handleSubmit = event => {
event.preventDefault();
this.setState({ isSubmitting: true });
const { formValues, formValidity } = this.state;
if (Object.values(formValidity).every(Boolean)) {
alert("Form is validated! Submitting the form...");
this.setState({ isSubmitting: false });
} else {
for (let key in formValues) {
let target = {
name: key,
value: formValues[key]
};
this.handleValidation(target);
}
this.setState({ isSubmitting: false });
}
};
Now the form is ready for use. React only provides the âviewâ layer for your application, and that means it provides only the basic necessities in making form components. component, state, and props are like puzzle blocks that you have to piece together to build a working form.
As you can see, itâs quite a lot of code for a form with only two text boxes. Imagine how many state values you need to keep track of in a form with 10 inputs or more. Yikes!
Yes, creating forms with React is no fun; itâs very verbose and rigid. Building the form and creating validation methods are boring tasks. In each form, youâd need to do the following, at a minimum:
Building forms the natural React way requires you to write every part of the process from setting up states to form submission. I have done countless React forms and I always find this part of building forms very boring and time-consuming. Fortunately, Iâm not the only one feeling that way.
Enter Formik. Jared Palmer authored the Formik library out of frustration when building React forms. He needed a way to standardize the input components and the flow of form submission. Formik helps you to write the three most annoying parts of building a form:
Here is the same form again, but this time using Formik. This new form only uses four extra components from Formik library: <Formik />, <Form />, <Field />, and <ErrorMessage />.
To unlock Formikâs power, you can wrap your form inside the <Formik /> component:
<Formik>
<Form>
{/* the rest of the code here */}
</Form>
</Formik>
Letâs see how Formik makes building forms easier compared to Reactâs natural way.
Formik will set up state internally for storing user inputs through its initialValues prop, so you donât need to initialize state from the constructor anymore.
To get values in and out of Formik internal state, you can use the <Field /> component to replace the regular HTML <input /> component. This component will do the magic of keeping Formik state and input value in sync, so you donât need to pass value and onChange props into the <Field /> component:
<Formik
initialValues={{ email: "", password: "" }}
onSubmit={({ setSubmitting }) => {
alert("Form is validated! Submitting the form...");
setSubmitting(false);
}}
>
{() => (
<Form>
<div className="form-group">
<label htmlFor="email">Email</label>
<Field
type="email"
name="email"
className="form-control"
/>
</div>
<div className="form-group">
<label htmlFor="password">Password</label>
<Field
type="password"
name="password"
className="form-control"
/>
</div>
</Form>
)}
</Formik>
handleChangeWith Formik, thereâs no need to initialize state in constructor and create your own handleChange method anymore. Itâs all taken care of.
Formik has its own handleChange method that you can use to update the state with user inputs, there is no need to implement your own handleChange method.
The handleChange method updates the form values based on the inputâs name attribute that was changed.
The handleChange method is used with input elements. Field component internally updates the values without the need of implementing the handleChange method.
Letâs demonstrate with an example:
const SignupForm = () => {
const formik = useFormik({
initialValues: {
email: "",
},
onSubmit: (values) => {
alert(JSON.stringify(values, null, 2));
},
});
return (
<form onSubmit={formik.handleSubmit}>
<label htmlFor="email">Email Address</label>
<input
id="email"
name="email"
type="email"
onChange={formik.handleChange}
value={formik.values.email}
/>
<button type="submit">Submit</button>
</form>
);
};
Here, we have an input with name set to email. See that we set an onChange event handler to it. The onChange event handler calls the handleChange method of the formik object.
This will update the formik objectâs values object with the new value so that we can retrieve it from the values object in the onSubmit props.
Validation is very essential when building forms. If forms are not handled properly, it can lead to a lot of errors.
Forms must tell users which fields are required, the type of values allowed in certain fields. It also helps to give users a clear indication of whatâs wrong with their input.
Formik provides a way to handle validation and display error messages. In the below sections, we will learn how to show validation messages in Formik and also how to show error messages in Formik.
Validation in Formik is executed automatically during specific events. All common events like after user input, on focus change, and on submit are covered, and you donât need to worry about them. All you need to do is pass a function into Formikâs validate prop.
Compare this code between Formik validation and vanilla React validation:
// Formik validation code. Take values from Formik
validate={values => {
let errors = {};
if (values.email === "") {
errors.email = "Email is required";
} else if (!emailTest.test(values.email)) {
errors.email = "Invalid email address format";
}
if (values.password === "") {
errors.password = "Password is required";
} else if (values.password.length < 3) {
errors.password = "Password must be 3 characters at minimum";
}
return errors;
}}
// Vanilla React validation code. Take values given by handleChange
handleValidation = target => {
const { name, value } = target;
const fieldValidationErrors = this.state.formErrors;
const validity = this.state.formValidity;
const isEmail = name === "email";
const isPassword = name === "password";
const emailTest = /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/i;
validity[name] = value.length > 0;
fieldValidationErrors[name] = validity[name]
? ""
: `${name} is required and cannot be empty`;
if (validity[name]) {
if (isEmail) {
validity[name] = emailTest.test(value);
fieldValidationErrors[name] = validity[name]
? ""
: `${name} should be a valid email address`;
}
if (isPassword) {
validity[name] = value.length >= 3;
fieldValidationErrors[name] = validity[name]
? ""
: `${name} should be 3 characters minimum`;
}
}
this.setState({
formErrors: fieldValidationErrors,
formValidity: validity
});
};
With validation in place, now you need to output error messages. Formikâs <ErrorMessage /> component will automatically display error messages for the <Field /> component with the given name.
You can adjust what HTML tag will be displayed through the component prop. Since this example form is using Bootstrapâs style, you must add a className prop as well:
// Formik error message output
<Field
type="email"
name="email"
className={`form-control ${
touched.email && errors.email ? "is-invalid" : ""
}`}
/>
<ErrorMessage
component="div"
name="email"
className="invalid-feedback"
/>
// Vanilla React error message output
<input
type="email"
name="email"
className={`form-control ${
formErrors.email ? "is-invalid" : ""
}`}
placeholder="Enter email"
onChange={this.handleChange}
value={formValues.email}
/>
<div className="invalid-feedback">{formErrors.email}</div>
The code for error message is actually about the same, but thereâs a lot less code in Formikâs validation than in vanilla React. Way to go, Formik!
Although you can already feel the benefit of using Formik in the validation process, you can make it even easier by using an object schema validator.
An object schema validator is simply a library that allows you to define the blueprint of a JavaScript object and ensure that the object values match that blueprint through the validation process.
This is particularly useful in validating form data since itâs actually an object kept inside Formikâs values prop.
Now one such library is Yup, and Formikâs author loves Yup so much that he included a special prop that connects Yup with Formik called validationSchema.
This prop automatically transforms Yupâs validation errors into a pretty object whose keys match values and touched.
Here is an example of Formik using Yup as its validation schema. Notice how the validate prop is removed from the <Formik /> component.
With Yupâs object schema validator in place, you donât have to manually write if conditions anymore. You can learn more about Yup and what kind of validation it can do by visiting its GitHub repo.
Formikâs <Form /> component will automatically run your validation method and cancel the submission process if there are any errors.
While you have to include the onSubmit prop to a regular <form /> element, Formikâs <Form /> wrapper will run the onSubmit prop function you passed into the <Formik /> component:
// Formik's submit code. Won't be executed if there are any errors.
onSubmit={({ setSubmitting }) => {
alert("Form is validated!");
setSubmitting(false);
}}
// Vanilla React submit code. Check on validity state then run validation manually.
handleSubmit = event => {
event.preventDefault();
this.setState({ isSubmitting: true });
const { formValues, formValidity } = this.state;
if (Object.values(formValidity).every(Boolean)) {
alert("Form is validated!");
this.setState({ isSubmitting: false });
} else {
for (let key in formValues) {
let target = {
name: key,
value: formValues[key]
};
this.handleValidation(target);
}
this.setState({ isSubmitting: false });
}
};
Formik requires only four lines of code for submission at a minimum, and you donât need to keep track of the validity of form inputs. Thatâs pretty neat!
onSubmitFormikâs onSubmit prop is a function that takes in the values prop and returns a promise. You can use this to do something with the values prop before the submission process:
// Formik's submit code.
onSubmit={(values, { setSubmitting }) => {
alert("Form is validated!");
setSubmitting(false);
}}
This function prop is quite similar to onsubmit in vanilla JavaScript. The onSubmit function is passed to your formâs values as the first argument and an object, which is an instance of FormikBag as the second argument:
onSubmit: (values: Values, formikBag: FormikBag) => void | Promise<any>
values contains the values of all the inputs in the form. The formikBag contains functions whose names start with set and reset, for example, setSubmitting and resetForm.
The onSubmit prop is useful because we can easily manipulate our form and its value from the arguments it is passed to. So, it gives us huge leverage over using HTML and form JavaScript APIs because it makes form manipulation easy.
Letâs see how we can use the onSubmit function with the Formik component:
// js
<Formik
initialValues={{ name: "jared" }}
onSubmit={(values, actions) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
actions.setSubmitting(false);
}, 1000);
}}
>
{(props) => (
<form onSubmit={props.handleSubmit}>
<input
type="text"
onChange={props.handleChange}
onBlur={props.handleBlur}
value={props.values.name}
name="name"
/>
{props.errors.name && <div id="feedback">{props.errors.name}</div>}
<button type="submit">Submit</button>
</form>
)}
</Formik>
Here, we set initial values using the initialValues prop. Then, we use the onSubmit function to do something with the values prop before the submission process.
We have a form element that calls the handleSubmit function when submitted. This submission event fires when clicking the button type="submit". Then, the onSubmit function in the Formik component is called.
One thing to note is that the onSubmit function is not called when the form is submitted. This is because Formikâs <Form /> wrapper will automatically run your validation method and cancel the submission process if there are any errors.
isSubmittingThis isSubmitting is a Boolean property set by Formik when a form goes through a submission process. You can use this to show a loading spinner or disable the submit button:
isSubmitting: boolean;
We can use this property to detect the state or the progress of the submission of the form. Letâs see an example of how to use this isSubmitting property:
const Example = () => (
<div>
<h1>Sign Up</h1>
<Formik
initialValues={{
email: "",
}}
onSubmit={async (values) => {
await sleep(500);
}}
>
{({ isSubmitting }) => (
<Form>
{isSubmitting && <div>Loading...</div>}
<label htmlFor="email">Email</label>
<Field name="email" placeholder="[email protected]" type="email" />
<button type="submit" disabled={isSubmitting}>
Submit
</button>
</Form>
)}
</Formik>
</div>
);
We see that we destructed the isSubmitting property from the Formik componentâs render props. We use the isSubmitting property to disable the submit button and show a LoadingâĶ indicator to the user.
Utilizing this isSubmitting property is great because it allows us to develop a nice UI/UX for our users and to tell them when background processes run in the background.
setSubmittingThe setSubmitting function sets the isSubmitting property of the Formikâs render props:
setSubmitting: (isSubmitting: boolean) =>
void;
<Formik
initialValues={{
email: "",
}}
onSubmit={async (values, { setSubmitting }) => {
// ...
}}
></Formik>
When this setSubmitting function is called with Boolean true, then isSubmitting is set to true. If the setSubmitting function is called with Boolean false, then isSubmitting is set to false.
So, we can see that the setSubmitting function sets the isSubmitting property of the Formikâs render props.
Letâs see an example:
const Example = () => {
return (
<div>
<h1>Sign Up</h1>
<Formik
initialValues={{
email: "",
}}
onSubmit={async (values, { setSubmitting }) => {
setSubmitting(true);
await sleep(500);
setSubmitting(false);
}}
>
{({ isSubmitting }) => (
<Form>
{isSubmitting && <div>Loading...</div>}
<label htmlFor="email">Email</label>
<Field name="email" placeholder="[email protected]" type="email" />
<button type="submit" disabled={isSubmitting}>
Submit
</button>
</Form>
)}
</Formik>
</div>
);
};
Here, we call the setSubmitting function with Boolean true when the form is submitted. This sets the isSubmitting to true and the button will be disabled and we see a Loading... indicator.
Then, the thread sleeps for 5 seconds and the setSubmitting is called with Boolean value false, which sets the isSubmitting to false and the button becomes disabled. The Loading... indicator finally goes away.
Sure, redux-form works great, but then youâd need to use Redux in the first place. What if youâre using MobX? What if a new, better library comes up in the future and you want to replace Redux with that?
On top of all that, does your React form actually affect the flow of your entire application in some way?
Think about it: Does the value of the username text box somehow matter to your application globally? If not, then itâs really not necessary to track its value using Redux. Even the prophet Dan Abramov said the same thing.
Another problem with redux-form is that you store form input values into Redux store. This means your application will call on Reduxâs reducer on every keystroke to update the value of just one textbox. Not a good idea.
I love writing forms the Formik way, but if you prefer redux-form, then thatâs fine, too.
Building forms is one of those things that React isnât good at. Luckily, React has a community of developers that help each other and make the process of writing code easier.
Formik is definitely one of those open source libraries thatâs a must-have if you are writing many forms in your React application.
It really speeds up your development process and reduces boilerplate code by abstracting away parts of your form through components like <Field /> and <Form />.
While a vanilla React form requires you to specify your own state values and methods, you can simply pass props to the <Formik /> component to do the same things: handle user inputs, validate inputs, and form submission.
If youâd like to learn more about Formik, head over to the documentation or watch the presentation below by its creator. Thanks for reading!
Install LogRocket via npm or script tag. LogRocket.init() must be called client-side, not
server-side
$ npm i --save logrocket
// Code:
import LogRocket from 'logrocket';
LogRocket.init('app/id');
// Add to your HTML:
<script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script>
<script>window.LogRocket && window.LogRocket.init('app/id');</script>

Learn how to use the TypeScript Compiler API and AST traversal to extract imports and build a file dependency graph CLI.

Stop generating AI slop with Claude Code. Discover 5 actionable developer tips to manage context windows, enforce rules with hooks, and improve code quality.

Choosing between skills and MCP tools comes down to auditability versus flexibility. By building the exact same capability twice, this guide reveals when your agent needs a deterministic tool and when it needs an interpretive skill.

Learn how to replace React state, Context, and event handlers with native HTML and CSS features for dark mode, modals, accordions, carousels, and more.
Hey there, want to help make our blog better?
Join LogRocketâs Content Advisory Board. Youâll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up now