Formik - Building React Forms easier
In this post you will learn what is Formik and why it is the best possible variant to create forms inside React.
Do we have a problem?
What problem do we have? Typically we have a form inside React application like this.
It's a registration form with 3 fields. Then we create markup for our form and add useState
for every property to store all fields inside component state.
<div>
<div className="field">
<input name="email" placeholder="Email" />
<div className="error"></div>
</div>
<div className="field">
<input name="username" placeholder="Username" />
<div className="error"></div>
</div>
<div className="field">
<input name="password" placeholder="Password" />
<div className="error"></div>
</div>
<button type="submit">Submit</button>
</div>
If you additionally want to client validation it is quite a lot of logic to write on your own and it doesn't make a lot of sense. Essentially inside forms it is always the same. We want a change event, a blur event, some errors and state of our form. It all can be done by the library.
It makes sense to write it on your own only if you have a super simple form that you don't need to reuse.
Which brings us to formik.
Inside React world we had one super popular solution previously which was redux-form. The main idea was that you have all your forms inside Redux and everything was reusable. But it is really slow to use Redux for forms if you have a lot of them because it is slower to update Redux state than just local state. This is why we have an alternative which is called Formik and this is just a local state and sugar to create local forms inside your component.
Basic Formik example
Let's implement Formik to our form now. Our first step will be to install a library.
npm install formik
As you saw above I have a basic React form which is pure HTML. We can use Formik in 2 different ways. First of all I want to show you how to use it with React Hooks.
import {useFormik} from 'formik'
const App = () => {
const formik = useFormik({
initialValues: {
email: "",
username: "",
password: "",
}
})
...
}
So here we defined a Formik with all default values of our form.
import {useFormik} from 'formik'
const App = () => {
const formik = useFormik({
initialValues: {
email: "",
username: "",
password: "",
},
onSubmit: (values) => {
console.log("onSubmit", values);
}
})
...
}
Now we added onSubmit
callback which will be called when our form is submitted. We also need to bind our inputs and form to formik.
<form onSubmit={formik.handleSubmit}>
<div className="field">
<input
name="email"
placeholder="Email"
value={formik.values.email}
onChange={formik.handleChange}
/>
<div className="error"></div>
</div>
<div className="field">
<input
name="username"
placeholder="Username"
value={formik.values.username}
onChange={formik.handleChange}
/>
<div className="error"></div>
</div>
<div className="field">
<input
name="password"
type="password"
placeholder="Password"
value={formik.values.password}
onChange={formik.handleChange}
/>
<div className="error"></div>
</div>
<button type="submit">Submit</button>
</div>
Here we provided formik.handleSubmit
to our form. We also set value
from formik in each input as well as onChange
event.
As you can see in looks exactly like typical form with React but instead of creating our own state we use formik.
Now in browser when we change our inputs and submit a form we see our console.log
with correct data of the form.
Adding validation
Now let's add some sugar to our formik. We want to show errors on blur so we need onBlur
event and render errors of each field.
<form onSubmit={formik.handleSubmit}>
<div className="field">
<input
name="email"
placeholder="Email"
value={formik.values.email}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
<div className="error">
{formik.errors.email && formik.touched.email && formik.errors.emai}
</div>
</div>
<div className="field">
<input
name="username"
placeholder="Username"
value={formik.values.username}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
<div className="error">
{formik.errors.username && formik.touched.username && formik.errors.username}
</div>
</div>
<div className="field">
<input
name="password"
type="password"
placeholder="Password"
value={formik.values.password}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
<div className="error">
{formik.errors.password && formik.touched.password && formik.errors.password}
</div>
</div>
<button type="submit">Submit</button>
</div>
We added onBlur
event to each input from formik and rendered a specific error for each field if we changed the value of the field.
But we still didn't define any validation. The idea is that we can define a validate
function inside formik which will be called every time when we change our form state.
const formik = useFormik({
...
validate: (values) => {
const errors = {};
if (!values.email) {
errors.email = "Email is required";
}
if (!values.username) {
errors.username = "Username is required";
}
if (!values.password) {
errors.password = "Password is required";
}
return errors;
},
})
The idea of validate
function is to return an object with errors which looks the same like the object with all fields. Here we just check each field for emptiness.
As you can see now we get nice errors out of the box but only when our fields are touched.
So formik generates errors in formik.errors
and we render them in specific containers.
In formik we don't write validation logic in submit. We simply define our validation rules.
Validation schema
But actually we can do it even better by using additional package. We can install a package which is called Yup
and it works nice with Formik.
npm install yup
import * as Yup from 'yup'
const formik = useFormik({
...
validationSchema: Yup.object({
email: Yup.string()
.required("Email is required")
.email("Invalid email adress"),
password: Yup.string().required("Password is required"),
username: Yup.string().required("Username is required"),
}),
})
Here we describe our validation by using specific rules. We don't have any logic but only declarative rules. As you can see in browser it works exactly like validate
function.
Adding formik magic
As you can see formik is really amazing and this is how I recommend you to use it. But if you want to use more sugar you need to use Formik in another way.
Our first step will be to move all properties from formik object to just separate constants.
const App = () => {
const initialValues = {
email: "",
username: "",
password: "",
};
const onSubmit = (values) => {
console.log("onSubmit", values);
};
const validationSchema = Yup.object({
email: Yup.string()
.required("Email is required")
.email("Invalid email adress"),
password: Yup.string().required("Password is required"),
username: Yup.string().required("Username is required"),
});
...
}
Now we can use components from Formik to write even less code.
<Formik
initialValues={initialValues}
onSubmit={onSubmit}
validationSchema={validationSchema}
>
{() => (
<Form>
<div className="field">
<Field name="email" placeholder="Email" />
<div className="error">
<ErrorMessage name="email" component="span" />
</div>
</div>
<div className="field">
<Field name="username" placeholder="Username" />
<div className="error">
<ErrorMessage name="username" component="span" />
</div>
</div>
<div className="field">
<Field name="password" placeholder="Password" type="password" />
<div className="error">
<ErrorMessage name="password" component="span" />
</div>
</div>
<button type="submit">Submit</button>
</Form>
)}
</Formik>
Here we used Formik
component as render props. We provided in Formik
initialValues
, onSubmit
and validationSchema
just like we did previously with hooks.
Also instead of our own inputs and error messages we used components from Formik. As you can see we don't need to provide value
, onChange
or anything else. We just give a name and Formik does the reset.
As you can see this approach is much cleaner to write code but we have less control to change things this is why I typically use hook approach.
Want to conquer your next JavaScript interview? Download my FREE PDF - Pass Your JS Interview with Confidence and start preparing for success today!
📚 Source code of what we've done