We all know that making and maintaining forms in React can be a bit of a pain, thankfully Formik is here to save us.
In this post we will go over how to use Formik in React, why you should use it, and when you should and shouldn’t use it.
I feel that before we get into how to use Formik, we first need to talk about why it is important, rather than just putting your form into a useState or useReducer react hook.
For those of you who just want to see how to use Formik in React, click here to skip to that part.
As with all my posts, I aim to write this post without too much technical jargon so that all experience levels can follow along and benefit from it.
Why use Formik?
Well let’s start by looking at the alternatives (although we won’t be considering other libraries here).
Option 1: Create a form using useState
Using the useState react hook is probably the fastest way you can implement a basic form in react. You can either put everything into a single state using an object or you can create multiple states each with their own form field.
If you were to go down this route, adding your form fields into a single state with the use of an object would probably best to avoid polluting your codebase with new useState hooks.
To do this you could create a helper method to manage adding, updating and removing the fields.
Whilst this might be fast to implement, it will become fairly un-maintainable pretty quickly and you will have to manually add all the onChange handlers to each and every field not to mention side effects, error handling and so on.
Option 2: Create a form using useReducer
Using the useReducer react hook is pretty much identical to the useState hook except that you can manage the form state a little better with more control rather than having to create a helper to update the state.
Option 3: Create your own form component/library
Finally this last option might be useful if you have a really bespoke need that something like Formik cannot solve for you, but for the most part this will just be re-inventing the wheel and you will spend a lot more time building out your form components/library, when you could instead use that time to read over the Formik docs.
Option 4: Creating your form using your redux store
This is probably the worst option of them all and there are very few occasions when this will ever be necessary.
It is a bad choice because when you use redux with a store as your single source of truth, the last thing you want to do is pollute it with single use form fields, and properties that either may never get used, or will only ever be used once and never need to be persisted across your application.
In this situation, once again having a local form state (like Formik) would work in your favour, and after the form gets submitted, you can then fire some actions with the resulting data so you only save what you need.
For the most part, these four options are not ideal and when you compare them with a plug and play library like Formik. You can start to see why you will want to go with Formik 99% of the time.
With that being said there are occasions when you might want to use other options which we will go over a bit later on.
How to use Formik in React?
So how do you use Formik in React?
It is pretty straightforward to get started with.
I won’t go over everything in Formik but if you want to have a read over the whole API then check out the official Formik docs here.
Step 1. Install
If you are using yarn, start by installing it into your project with:
yarn add Formik
And if you are using NPM:
npm install Formik --save
Once we have that installed we are going to create a basic component that uses Formik with a few form fields to initialise the form and tell Formik what we need:
import React from "react"
import { Formik } from "formik"
function buyApples(applesAmount) {
alert(`You have purchased ${applesAmount} apples`)
}
export default function ExampleFormikForm() {
return (
<Formik
initialValues={{
applesAmount: 0,
}}
onSubmit={values => {
buyApples(values.applesAmount)
}}
>
{({ handleChange, handleBlur, values, handleSubmit }) => (
<form onSubmit={handleSubmit}>
<label htmlFor="applesAmount">
<span>Amount of apples:</span>
<input
name="applesAmount"
type="number"
onChange={handleChange}
onBlur={handleBlur}
value={values.applesAmount}
required
/>
</label>
<button type="submit">Buy apples</button>
</form>
)}
</Formik>
)
}
As you can see we have our component which contains our Formik form which has an input where you can enter the number of apples you want to buy.
Aside from that we have a submit button as well as a function buyApples
which gets called when the form is submitted successfully.
From Formik we are making use of handleChange
, handleBlur
, values
, and handleSubmit
which are all fairly self explanitory.
When a user enters a number and then submits, it triggers our buy apples function and displays an alert.
Form Validation:
Now we have our basic form, we need some simple validation on the data we send. To do this we are going to use a library called yup
.
Install Yup
by running yarn add yup
or npm install yup --save
just like we did with Formik.
Now lets add our basic validation:
import * as yup from "yup"
const appleFormValidation = yup.object().shape({
applesAmount: yup.number().required().positive().integer(),
})
As you can see here we are using yup to define our form validation by saying that our applesAmount
must be a positive whole number that is required.
Let’s look at how we can now add this into our form:
import React from "react"
import { Formik } from "formik"
import * as yup from "yup"
function buyApples(applesAmount) {
alert(`You have purchased ${applesAmount} apples`)
}
const appleFormValidation = yup.object().shape({
applesAmount: yup.number().required().positive().integer(),
})
export default function ExampleFormikForm() {
return (
<Formik
initialValues={{
applesAmount: 0,
}}
validationSchema={appleFormValidation}
onSubmit={values => {
buyApples(values.applesAmount)
}}
>
{({ handleChange, handleBlur, values, handleSubmit }) => (
<form onSubmit={handleSubmit}>
<label htmlFor="applesAmount">
<span>Amount of apples:</span>
<input
name="applesAmount"
type="number"
onChange={handleChange}
onBlur={handleBlur}
value={values.applesAmount}
required
/>
</label>
<button type="submit">Buy apples</button>
</form>
)}
</Formik>
)
}
Here we are only adding in one prop into Formik which is validationSchema={appleFormValidation}
that applies our validation to the form.
If we now try to enter a string or a negative number our buyApples
function will never be called and we will never see the alert.
This means our validation is working!
And there we have our basic form with validation!
As you can see it is really easy to set up and to create forms with.
The next thing we need to look into is handling errors so it doesnt just scilently fail like in the above.
How to handle errors with Formik in react:
Handling errors in Formik is nice and easy, Formik provides an easy to use prop called errors
.
All we need to do is check whether an error exists, and if it does then display it to the user.
To do this we first need to get the following properties from our Formik props: errors, touched
And then, we need to add in the following code to our form (just above our input label):
{
errors.applesAmount && touched.applesAmount && (
<p style={{ color: "red" }}> {errors.applesAmount} </p>
)
}
Here is this our error handling code integrated with our Formik form:
import React from "react"
import { Formik } from "formik"
import * as yup from "yup"
function buyApples(applesAmount) {
alert(`You have purchased ${applesAmount} apples`)
}
const appleFormValidation = yup.object().shape({
applesAmount: yup.number().required().positive().integer(),
})
export default function ExampleFormikForm() {
return (
<Formik
initialValues={{
applesAmount: 0,
}}
validationSchema={appleFormValidation}
onSubmit={values => {
buyApples(values.applesAmount)
}}
>
{({
handleChange,
handleBlur,
values,
handleSubmit,
errors,
touched,
}) => (
<form onSubmit={handleSubmit}>
{errors.applesAmount && touched.applesAmount && (
<p style={{ color: "red" }}> {errors.applesAmount} </p>
)}
<label htmlFor="applesAmount">
<span>Amount of apples:</span>
<input
name="applesAmount"
type="number"
onChange={handleChange}
onBlur={handleBlur}
value={values.applesAmount}
/>
</label>
<button type="submit">Buy apples</button>
</form>
)}
</Formik>
)
}
As you can see that only took a few seconds to add in. But we can do this better by making our own error handling component in combination with the useFormikContext
hook (don’t worry if you are not familiar with hooks in Formik because we will go into them a little later on).
If we do this we can create a reusable component that we can plug and play in any Formik form.
Here is a basic example of how you could create and use this component:
import React from "react"
import { useFormikContext } from "formik"
export default function HandleError({ name }) {
const { errors, touched } = useFormikContext()
if (errors?.[name] && touched?.[name]) {
return <p style={{ color: "red" }}> {errors[name]} </p>
}
return null
}
And in our form:
import React from "react"
import { Formik } from "formik"
import * as yup from "yup"
import HandleError from "./HandleError"
function buyApples(applesAmount) {
alert(`You have purchased ${applesAmount} apples`)
}
const appleFormValidation = yup.object().shape({
applesAmount: yup.number().required().positive().integer(),
})
export default function ExampleFormikForm() {
return (
<Formik
initialValues={{
applesAmount: 0,
}}
validationSchema={appleFormValidation}
onSubmit={values => {
buyApples(values.applesAmount)
}}
>
{({
handleChange,
handleBlur,
values,
handleSubmit,
errors,
touched,
}) => (
<form onSubmit={handleSubmit}>
<HandleError name="applesAmount" />
<label htmlFor="applesAmount">
<span>Amount of apples:</span>
<input
name="applesAmount"
type="number"
onChange={handleChange}
onBlur={handleBlur}
value={values.applesAmount}
/>
</label>
<button type="submit">Buy apples</button>
</form>
)}
</Formik>
)
}
This will work in the same way as before, but now we do not need to pass down any props except from the name of the input we are displaying an error for.
How to use Formik hooks in react:
Let’s now take a look at using hooks in Formik to make slightly more advanced forms and ever create your own form component.
useField:
Let’s start by creating a custom input component, that can include the label and error handling, rather than just using a html input.
import { useField } from "formik"
import HandleError from "./HandleError"
export default function CustomFormInputExample({ name, label, ...props }) {
const [field] = useField({ name, ...props })
return (
<>
<HandleError name={name} />
<label htmlFor={name}>
<span>{label}</span>
<input {...field} {...props} />
</label>
</>
)
}
Now all we have to do is to plug this into our Formik form:
import React from "react"
import { Formik } from "formik"
import * as yup from "yup"
import CustomFormInputExample from "./CustomFormInputExample"
export default function ExampleFormikForm() {
return (
<Formik
initialValues={{
applesAmount: 0,
}}
validationSchema={appleFormValidation}
onSubmit={values => {
buyApples(values.applesAmount)
}}
>
{({ handleSubmit }) => (
<form onSubmit={handleSubmit}>
<CustomFormInputExample
name="applesAmount"
label="Amount of apples:"
/>
<button type="submit">Buy apples</button>
</form>
)}
</Formik>
)
}
Now as you can see, we have our form with our input components and everything should be working as it was.
But what if we want to have a slightly more complex input?
We need to create our own Formik form component using hooks.
For this, let’s say we want one of our inputs to add the input text into an array when the user clicks a button next to the input.
Let’s take a look at how we can go about doing that:
import { useField } from "formik"
import React, { useState } from "react"
import styles from "./MultipleEntryInputExample.module.css"
export default function MultipleEntryInputExample({ label, ...props }) {
const [nextValue, setNextValue] = useState({ name: "", value: "" }) // this will control the internals of this component
const [{ value: fieldValue }, , { setValue }] = useField(props) // this is where we store all our entries
return (
<div className={styles.container}>
<h3>{label}</h3>
<div className={styles.multiInputs}>
<input
label="Name"
className={styles.input}
name={`multi-name-${props.name}`}
onChange={e => setNextValue(s => ({ ...s, name: e.target.value }))}
value={nextValue.name}
/>
<input
label="Value"
className={styles.input}
name={`multi-value-${props.name}`}
onChange={e => setNextValue(s => ({ ...s, value: e.target.value }))}
value={nextValue.value}
/>
<button
className={styles.button}
onClick={() => {
if (!nextValue.name && !nextValue.value) {
return
}
const next = {
name: nextValue?.name || nextValue.value,
value: nextValue?.value || nextValue.name,
}
setValue([...fieldValue, next]) // add new entry to exsiting array
setNextValue({ name: "", value: "" }) // after adding to the array we reset the internal state
}}
>
<span>+</span>
</button>
</div>
<ul className={styles.list}>
{fieldValue.map(({ name, value }) => (
<li className={styles.multiInputs} key={value}>
<input value={name} className={styles.inputAdded} disabled />
<input value={value} className={styles.inputAdded} disabled />
<button
className={styles.button}
onClick={() => {
setValue(fieldValue?.filter(entry => entry.value !== value))
}}
>
<span className={styles.minus}>-</span>
</button>
</li>
))}
</ul>
</div>
)
}
As you can see, we have now made use of one of a Formik hook called useField
to get and set the array in the Formik form.
The useField hook will take the name we give it that will relate to a form property, and then return us the value of the field and the setValue function to update the value in the form.
We also have a useState hook in our component to keep track of the name and value of the new entry that the user is typing into the input field.
When the user clicks on the button, we add the new entry to the form, and then clear the input state.
So the state here is controlling the input element and the form is controlling the array of data that is updated from our input.
This hook will probably be the one you use most often, but there is another hook that you may end up making use of called useFormikContext.
useFormikContext:
The useFormikContext
hook gives you access to the form state/props that is passed into the children of the Formik
component.
Just like how we used this earlier when we created our error handling component.
When to use Formik?
The last topic that we should cover here is: when to use Formik in React?
The answer to this will be largely circumstantial and personal to each use case and each developer.
With that being said, the short answer would be: 99% - 100% of the time. So if you are in doubt, go ahead and use Formik.
Given that it will be highly dependent on the project, and the form you are using and assuming that the alternatives are like we stated before (useState, useReducer, redux, writing your own version) I will generalise and say that for the most part, it is safe to assume that you should just use Formik.
There might be a few cases like if you have a very simple form with only one input and your project is not already using Formik then it might not be worth adding in Formik if this is the only place you would need to use it.
So to summarise when to use Formik in react, it is safe to assume that most of the time, if not always (when compared with the alternatives above), you should just go ahead and use it.
Summarising how to use Formik in React.
Formik is an amazing tool that saves a lot of time, effort and hassle when building forms in React.
Not to mention, it handles all the state, and getting, and setting of that state so it reduces the amount of code you have to write and maintain considerably.
It has built in validation that can be supplemented with the Yup library which makes life even easier.
Formik hooks means it is simple and easy to write custom form components and write forms with higher complexity as well.
Will