Tutorial: How to Reset Formik Form
Jasser Mark Arioste
In one of my projects, I recently had to implement a functionality where in the backend/cms where the save button will be disabled after submitting the changes. This can be done by using the really awesome react library called Formik
.
What is Formik? #
Formik is a relatively small react library used for managing forms in react. It allows us to build forms and manage form state without the headache. This is especially useful if you're building a huge form like for example in the backend Edit user, Edit post, etc. A post or a user has many properties just managing the errors or state of the page is borderline impossible without something like Formik.
One thing I like about Formik is it's simplicity when integrating with UI libraries like MUI.
How to reset the form and disable the save button? #
One way to disable the save button is to check if the form is dirty using dirty
flag from formik. dirty
will be set to true
when there's any change in the initial state of the form. The way to modify dirty
is by calling resetForm
helper function provided by Formik
.
In this tutorial I'll be discussing the following scenarios:
- resetting the form inside onSubmit function
- Resetting the Form when using <Formik> provider
- Resetting the Form when using isFormik hook
Scenario 1: Resetting the form inside onSubmit function #
In simple scenarios, we can just reset the form inside onSubmit
. For example let's see the form below:
//EditUserForm.tsx import { Field, Form, Formik } from "formik"; import React from "react"; import SaveButton from "./SaveButton"; const EditUserForm = () => { //just a mock save function, let's pretend this calls the server const save = async () => { return { error: Math.random() > 0.5 ? "failed to save" : "", }; }; return ( <Formik onSubmit={async (values, helpers) => { const { error } = await save(); // reset the form if it is saved successfully //now the initial state will be the values submitted. if (!error) { helpers.resetForm({ values, }); } }} initialValues={{ name: "John Doe", email: "johndoe@gmail.com" }} > <Form className="flex flex-col gap-2 max-w-lg bg-base-200 shadow-md p-10"> <h1 className="text-xl"> Edit User</h1> <Field name="name" type="text" placeholder="name" className="input input-bordered" /> <Field name="email" type="email" placeholder="email" className="input input-bordered" /> {/* custom save button */} <SaveButton /> </Form> </Formik> ); }; export default EditUserForm;
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051
//SaveButton.tsx import { useFormikContext } from "formik"; import React from "react"; const SaveButton = () => { //access formik context to check if the form is dirty. const { dirty } = useFormikContext(); return ( <> <button type="submit" className="btn btn-primary" disabled={!dirty}> Save </button> </> ); }; export default SaveButton;
123456789101112131415161718
In the codes above we have two components, EditUserForm
and SaveButton
. In this specific use case, The submit button has to be disabled when there are no changes to the fields and when the save is successful. It only becomes enabled when the user edits the fields.
Let's see it in action.
Notice the save button. Once we click save the form is reset but we're using the updated values as the initial values. We achieved this by passing an { values }
option to the resetForm
method. If we want to reset the form using the original values, we don't have to pass anything to resetForm
.
With this I've solved my use case.
Now let's talk about other use cases. Let's say we don't want to reset the form inside onSubmit
and just have a reset button inside the form so that the user can go back to the initial values. There are two ways to use formik and let's discuss how to achieve this in both ways:
Scenario 2: Resetting the form when using <Formik>
provider.
#
Almost 100% of the time, I use <Formik>
provider when creating forms because it has a clear boundary of the scope/context of form. You can have nested <Formik>
provider components and there will be no problem.
So in order to access resetForm helper, there are two ways:
- Using renderProps
- Creating a new component and using useFormikContext
Using renderProps to access Formik Context
Let's modify our form component and add a reset button beside the save button. The reset button should only be enabled if the form is dirty and when click it resets the form to it's original state.
//EditUserForm.tsx import { Field, Form, Formik } from "formik"; import React from "react"; import SaveButton from "./SaveButton"; const EditUserForm = () => { ... return ( <Formik > ... {/* we can use render props to get a hold of the formik context */} {({ dirty, resetForm }) => { return ( <Form className="flex flex-col gap-2 max-w-lg bg-base-200 shadow-md p-10"> <h1 className="text-xl"> Edit User</h1> <Field name="name" type="text" placeholder="name" className="input input-bordered" /> <Field name="email" type="email" placeholder="email" className="input input-bordered" /> <div className="flex gap-2 "> <button className="btn btn-outline btn-error" disabled={!dirty} onClick={() => resetForm()} > Reset </button> <SaveButton /> </div> </Form> ); }} </Formik> ); }; export default EditUserForm;
12345678910111213141516171819202122232425262728293031323334353637383940414243444546
This already gives us the functionality we need but I prefer refactoring it using next method since it's a bit cleaner than using renderProps.
Creating a new component and using useFormikContext
Let's create a new component called ResetButton.tsx
and use useFormikContext
hook to access the form state and helper functions from inside the component.
//ResetButton.tsx import { useFormikContext } from "formik"; import React from "react"; const ResetButton = () => { //access formik context to check if the form is dirty. const { dirty, resetForm } = useFormikContext(); return ( <> <button type="submit" className="btn btn-primary" disabled={!dirty} onClick={() => resetForm()} > Reset </button> </> ); }; export default ResetButton;
12345678910111213141516171819202122
Usage inside EditUserForm.tsx
//EditUserForm.tsx import { Field, Form, Formik } from "formik"; import React from "react"; import ResetButton from "./ResetButton"; import SaveButton from "./SaveButton"; const EditUserForm = () => { ... return ( <Formik> <Form className="flex flex-col gap-2 max-w-lg bg-base-200 shadow-md p-10"> <h1 className="text-xl"> Edit User</h1> <Field name="name" type="text" placeholder="name" className="input input-bordered" /> <Field name="email" type="email" placeholder="email" className="input input-bordered" /> <div className="flex gap-2 "> <ResetButton /> <SaveButton /> </div> </Form> </Formik> ); }; export default EditUserForm;
1234567891011121314151617181920212223242526272829303132333435
Scenario 3: Resetting the form when using useFormik
hook
#
If you're using useFormik
to initialize your form, you can simply call resetFrom
from the returned context. It's just preference but I don't like to use this method since you have to wire up the fields and form.
//EditUserFormHooks.tsx import { Field, Form, Formik, useFormik } from "formik"; import React from "react"; import SaveButton from "./SaveButton"; const EditUserFormHooks = () => { //just a mock save function, let's pretend this calls the server const save = async () => { return { error: Math.random() > 0.5 ? "failed to save" : "", }; }; const formik = useFormik({ onSubmit: async (values, helpers) => { const { error } = await save(); if (!error) { helpers.resetForm({ values, }); } }, initialValues: { name: "John Doe", email: "johndoe@gmail.com" }, }); return ( <form className="flex flex-col gap-2 max-w-lg bg-base-200 shadow-md p-10" onSubmit={formik.submitForm} > <h1 className="text-xl"> Edit User</h1> <input name="name" type="text" placeholder="name" className="input input-bordered" onChange={formik.handleChange} value={formik.values.name} /> <input name="email" type="email" placeholder="email" className="input input-bordered" onChange={formik.handleChange} value={formik.values.email} /> <div className="flex gap-2 "> {/* check formik diry and reset form here */} <button type="button" className="btn btn-outline btn-error" disabled={!formik.dirty} onClick={() => formik.resetForm()} > Reset </button> <button type="submit" className="btn btn-primary" disabled={!formik.dirty} > Save </button> </div> </form> ); }; export default EditUserFormHooks;
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
Final output #
Conclusion #
We've successfully reset our form using Formik
by using <Formik>
provider or useFormik
hook. Formik
is very useful when managing forms in react! To learn more about formik you can check the docs here.
Code is available here: https://github.com/jmarioste/formik-reset-form
If you like tutorials like this, please subscribe to our newsletter down below! If not, give it a like or share!
Credits: Image by Pete Linforth from Pixabay