The Formik library helps to build React forms faster with its state management and component. This tutorial will help you learn how to build a React form using components provided by Formik library. You will also learn how to create a simple validation schema for forms with Yup library.
A quick intro
This short series is about three ways to build React forms with Formik. In the first part, we've taken a look at the first way. We focused on build React forms with Formik using only the bare essentials Formik provides. In this part, we will lean much more towards Formik and its components.
Generally speaking, we can use Formik to build React forms with two approaches. The first one is with React components. The second is by using Formik hooks. We've already, partially, explored the first approach in the first part. In this part, we will take a look at this approach one more time, now using Formik components instead of custom.
A word on dependencies
This tutorial will use minimal number of dependencies. First, we will need the react
, react-dom
and react-scrips
. These three will help as get the React app from the ground. You can either install these dependencies by yourself or use the create-react-app app to setup everything for you.
When you have the React app ready, you two additional dependencies. The first one will be Formik
, library that will power our form. The second dependency will be Yup
. This is a validation library that will help as create validation schema for our form. We will talk about this in the next section, "Validation schema".
In this tutorial, we will use react
and react-dom
version 17.0.2
. The react-scrips
will be version 4.0.3
. Formik will be version 2.2.9
. Version of Yup will be 0.32.9
. When you install these dependencies you are ready to go.
Validation schema
For this part, we will use the same validation schema as we used in the previous part. This validation schema will contain three form fields, "name", "email" and "password". All these fields will be strings and all will be required. For the "email" field, we will want to check that any value user provides is in email format.
We could build this schema, and necessary validation logic by ourselves and connect it with Formik. We don't have to. Instead, we can use available validation libraries to do this work for us. One of these libraries is [Yup]. Thanks to this library, we can create validation schema objects Formik can use to validate all fields on our forms.
A nice thing on Yup is that it provides various methods we can use to create validation schema that fits our needs. For example, we can use method string()
to specify that some field value must be a string. We can then make it required by using required()
method. To make sure something is in email format?
Yup provides method email()
that checks if the value passed into the input is in email format or not. There are many other methods, and customizations, ready to use. For this tutorial, we will stick with these three, string()
, required()
and email()
. Yup also allows to define error messages for each field.
In a fact, we can define error message for each of Yup's validation methods. What this means is that we can display different message for the same field. What message will be visible will depend on current error. Creating these messages is easy. All we need is to pass these messages as strings to specific Yup method.
For example, we can define that field is required and specify a simple message for this condition: required('Field "X" is required')
. We will use this feature in our schema as well and define different message for different validations. Nonetheless, the validation will remain simple.
// Import Yup:
import * as Yup from 'yup'
// Create validation schema for form
// with three fields: "name", "email" and "password":
const formSchema = Yup.object().shape({
name: Yup.string().required('First name is required'),
email: Yup.string().email('Invalid email').required('Email is required'),
password: Yup.string().required('Password is required'),
})
Using Formik and its components
The schema is ready. Now, let's build React form using mainly Formik components. The upside of this approach is that we will not have to specify as many input element attributes as we had to in the previous part. This is because all form components are provided by Formik and as such are automatically connected to Formik parent instance (context).
Formik components we will need will be three, Formik
, Form
and Field
. The Form
and Field
will be replacements for HTML form
and input
elements. The Formik
component will create a parent instance and context of our Formik form. This will be the wrapper of the whole form, this includes the Form
component.
Formik component
The Formik
component has multiple attributes we can use to setup and customize Formik functionality. For the purpose of this tutorial we will need three: initialValues
, onSubmit
and validationSchema
. The initialValues
attribute is on object that allows as to define all form fields and their initial values.
For our form, we will specify properties of initialValues
(form fields) to be "name", "email" and "password". All initial values will be empty strings. As a value for the validationSchema
attribute we will use the validation schema we created with Yup. The value of onSubmit
will be a function Formik will use when form is submitted.
For this tutorial, we will create arrow function with console.log()
to log values provided to the form. In your case, this is the place where you can add any logic you want to execute when someone submits the form. There is one more thing we will need from Formik. We will need access to errors
and touched
objects.
These two are Formik's states that keep tract of fields that contain any errors and fields that have been touched, or focused. We can expose this data from Formik very easily. This is because Formik
component uses render-prop pattern, and allows its children to be a function that returns some React component.
This rendered component will be our form. What we can do is to tell Formik
component to expose some data by passing them as arguments to the function it renders. This will allow use to use this exposed data anywhere in the form. Data we will pass are the errors
and touched
objects. We will pass them using object destructuring.
// Import dependencies:
import { memo } from 'react'
import { Formik, Form, Field } from 'formik'
// Create the form component:
export const FormFormik = memo(() => {
return (
<Formik
initialValues={{ name: '', email: '', password: '' }}
onSubmit={(values) => {
console.log(values)
}}
validationSchema={formSchema}
>
{({ errors, touched }) => (
<Form>{/* The rest of the form content */}</Form>
)}
</Formik>
)
})
FormFormik.displayName = 'FormFormik'
Field components and error messages
Each form field will be composed of three parts: label, field and error message. We will create the label and error message with label
and p
HTML element. This is because Formik doesn't render labels nor provide a dedicated component for it. It renders only input placeholders if you tell it to do so.
So, if you want to use input placeholders instead of labels you can ignore the label
elements. Instead of label
, you can add placeholder
attribute for each field with appropriate text. Another two attributes we will need will be type
and name
. The type
is the same as input type
attribute that specifies the type of input.
The name
is also the same as input name
attribute. Aside to that, it also allows Formik to connect field with correct value in form states. This includes initialValues
, errors
and touched
. This means that value of name
for each field has to match corresponding property in initialValues
, errors
and touched
and also in validation schema.
So, if our schema contains rules for fields "name", "email" and "password", values for name
attributes has to be one of these, name
, email
and password
. That's all we need, or Formik needs, for the Field
. No need for additional attributes or handlers. Last piece are error messages.
We will render error messages as plain text wrapped in p
elements. The important thing here is the rendering condition for each message. We want to display errors only when there are any and when user really interacted with the form. We want to avoid showing errors in an empty form that was just loaded.
To ensure this, we will use the errors
and touched
objects. For each field, we will first check if there are any errors for that field. We will also check if a field has been touched. Only when field has an error and was touched we will show an error. We will get these information by using the value of name
attribute.
Last thing. We will need a button to submit the form. This can be a regular HTML button
element with type
set to submit
. When clicked, this will trigger Formik's onSubmit
method. This is the method you pass as the value to onSubmit
attribute of Formik
component.
// ... previous code
<Form>
<div>
<label htmlFor="name">Name</label>
{/* Create field component - renders input element */}
<Field type="text" name="name" />
{/* Show error if field contains error and was touched */}
{errors.name && touched.name && <p>{errors.name}</p>}
</div>
<div>
<label htmlFor="email">Email</label>
{/* Create field component - renders input element */}
<Field type="email" name="email" />
{/* Show error if field contains error and was touched */}
{errors.email && touched.email && <p>{errors.email}</p>}
</div>
<div>
<label htmlFor="password">Password</label>
{/* Create field component - renders input element */}
<Field type="password" name="password" />
{/* Show error if field contains error and was touched */}
{errors.password && touched.password && <p>{errors.password}</p>}
</div>
<div>
<button type="submit">Submit</button>
</div>
</Form>
// ... rest of the code
Putting it together
The Formik
component is ready and Field
components for each field, with error message, are ready as well. The thing that remains is to take the code we've created so far and put it together. This will give us working React form powered by Formik components, and validated by Yup.
// Import dependencies:
import { memo } from 'react'
import { Formik, Form, Field } from 'formik'
import * as Yup from 'yup'
// Create form validation schema:
const formSchema = Yup.object().shape({
name: Yup.string().required('First name is required'),
email: Yup.string().email('Invalid email').required('Email is required'),
password: Yup.string().required('Password is required'),
})
// Create the form component:
export const FormFormik = memo(() => {
return (
<Formik
initialValues={{ name: '', email: '', password: '' }}
onSubmit={(values) => {
console.log(values)
}}
validationSchema={formSchema}
>
{({ errors, touched }) => (
<Form>
<div>
<label htmlFor="name">Name</label>
{/* Create field component - renders input element */}
<Field type="text" name="name" />
{/* Show error if field contains error and was touched */}
{errors.name && touched.name && <p>{errors.name}</p>}
</div>
<div>
<label htmlFor="email">Email</label>
{/* Create field component - renders input element */}
<Field type="email" name="email" />
{/* Show error if field contains error and was touched */}
{errors.email && touched.email && <p>{errors.email}</p>}
</div>
<div>
<label htmlFor="password">Password</label>
{/* Create field component - renders input element */}
<Field type="password" name="password" />
{/* Show error if field contains error and was touched */}
{errors.password && touched.password && <p>{errors.password}</p>}
</div>
<div>
<button type="submit">Submit</button>
</div>
<div>
<button type="submit">Submit</button>
</div>
</Form>
)}
</Formik>
)
})
FormFormik.displayName = 'FormFormik'
Conclusion: 3 ways to build React forms with Formik pt.2
This was the alternative of the first approach of using Formik to build React forms. This approach, using mainly Formik components, can reduce HTML markup you would otherwise need. With some validation library such as Yup, you can also remove a lot of code you would otherwise need for validation logic. I hope that this tutorial helped you learn how to do both.
Top comments (0)