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:
handleChange
onSubmit
isSubmitting
setSubmitting
Before 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>
handleChange
With 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!
onSubmit
Formik’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.
isSubmitting
This 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.
setSubmitting
The 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>
Would you be interested in joining LogRocket's developer community?
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 nowValidating and auditing AI-generated code reduces code errors and ensures that code is compliant.
Build a real-time image background remover in Vue using Transformers.js and WebGPU for client-side processing with privacy and efficiency.
Optimize search parameter handling in React and Next.js with nuqs for SEO-friendly, shareable URLs and a better user experience.
Learn how Remix enhances SSR performance, simplifies data fetching, and improves SEO compared to client-heavy React apps.
2 Replies to "Building forms with Formik in React"
Hello, I think I found a mistake in your codesandbox:
This line:
onSubmit={({ setSubmitting }) => {
Must be written like this:
onSubmit={(values, { setSubmitting }) => {
right?
Hello, i think in the following paragraph
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 last phrase should be “which sets the isSubmitting to false and the button becomes enabled”