It’s not often that you write a web app that doesn’t contain at least one form. More often than naught, your entire app is just a series of forms. Each of these forms requires state management, event handlers, and, oftentimes, some sort of client-side data validation. Formik aims to do all of that in a small package and, as they claim, “without the tears”.
To get started with formik
we need to add it to our project via npm
or yarn
:
$ npm install --save formik
# or
$ yarn add formik
After the dependency has been added, be sure to import it into your project where you’re going to use it. For this article we’re going to use both Formik
and the Form
helper component:
import { Formik, Form } from "formik";
The Form
component is a wrapper for the standard form
element that automatically wires up the onSubmit
handler attached to the Formik
component, saving us even more time.
The real magic of Formik happens in the Formik
component. This component will be used to wrap your form up and exposes state values and handlers via the render props.
Said component can take properties to set up our default values, validate the submitted values and handle our submission.
Here’s how a Formik
component will look for a basic login form that we’ll be creating later on:
<Formik
// Sets up our default values
initialValues={{ email: "", password: "" }}
// Validates our data
validate={values => {
const errors = {};
if (!values.email) errors.email = "Required";
if (
!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(values.email)
) {
errors.email = "You must supply a valid email address";
}
if (values.password.length < 8) {
errors.password = "Passwords must be at least 8 characters";
}
if (values.email === values.password) {
errors.password =
"Your password shouldn't be the same as your email";
}
return errors;
}}
// Handles our submission
onSubmit={(values, { setSubmitting }) => {
// This is where you could wire up axios or superagent
console.log("Submitted Values:", values);
// Simulates the delay of a real request
setTimeout(() => setSubmitting(false), 3 * 1000);
}}
>
{props => (
<div>This is where your Form and form elements will go!</div>
)}
</Formik>
It’s worth nothing, if you’re familiar with object schema validators like yup
or joi
you can skip the validate
property and pass in a validationSchema
instead.
As mentioned, the Form
component is a drop-in replacement for a form
element that automagically wires up things like the onSubmit
handler.
Use it to wrap your form’s inputs like you normally would:
<Formik>
{props => (
<Form>
<label>My Input</label>
<input type="text" />
</Form>
)}
</Formik>
Out of the box, Formik’s render property exposes event handlers to manage changes to your form inputs, whether or not they have been “touched”, their values and any errors.
For the form as a whole, you will be able to tell if the form is in the process of being validated or submitted and an event handler that lets you easily reset the form.
props.values
and props.errors
are objects that have properties that correspond with your form fields.
props.handleChange
and props.handleBlur
can be passed to onChange
and onBlur
properties to track changes and whether or not an input has been “touched”.
This “touched” value comes in handy when you only want to show errors after a user has interacted with an element, instead of at the time of page load (which is preferred IMHO).
props.dirty
gets set to true when the form has been modified by the user.
State values props.isValidating
and props.isSubmitting
let you know what stage of the process the user is in. Perfect for displaying a loader or disabling the form or individual buttons.
Here’s what a simple login form with email and password looks like once it’s been formally Formiked:
<Formik
// Sets up our default values
initialValues={{ email: "", password: "" }}
// Validates our data
validate={values => {
const errors = {};
if (!values.email) errors.email = "Required";
if (
!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(values.email)
) {
errors.email = "You must supply a valid email address";
}
if (values.password.length < 8) {
errors.password = "Passwords must be at least 8 characters";
}
if (values.email === values.password) {
errors.password =
"Your password shouldn't be the same as your email";
}
return errors;
}}
// Handles our submission
onSubmit={(values, { setSubmitting }) => {
// This is where you could wire up axios or superagent
console.log("Submitted Values:", values);
// Simulates the delay of a real request
setTimeout(() => setSubmitting(false), 3 * 1000);
}}
>
{props => (
<Form>
<label htmlFor="email">Email</label>
<div>
<input
name="email"
type="email"
placeholder="Enter your account email"
value={props.values.email}
onChange={props.handleChange}
onBlur={props.handleBlur}
style={{
borderColor:
props.errors.email && props.touched.email && "red"
}}
/>
{props.errors.email && props.touched.email && (
<div style={{ color: "red" }}>{props.errors.email}</div>
)}
</div>
<label htmlFor="password">Password</label>
<div>
<input
name="password"
type="password"
placeholder="Enter your account password"
value={props.values.password}
onChange={props.handleChange}
onBlur={props.handleBlur}
style={{
borderColor:
props.errors.password && props.touched.password && "red"
}}
/>
{props.errors.password && props.touched.password && (
<div style={{ color: "red" }}>{props.errors.password}</div>
)}
</div>
<input
type="submit"
value="Submit"
disabled={props.isSubmitting}
/>
<input
type="reset"
value="Reset"
onClick={props.handleReset}
disabled={!props.dirty || props.isSubmitting}
/>
</Form>
)}
</Formik>
Formik provides a familiar, render prop-based approach to building forms.
Most of the redundant boilerplate and statement management logic you usually end up writing is taken care of nicely while still providing enough power under the hood to do some pretty complex state management on your forms.
If you’d like to give the code from this article a test spin, you can check it out over on CodeSandbox.
Cheers!
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
While we believe that this content benefits our community, we have not yet thoroughly reviewed it. If you have any suggestions for improvements, please let us know by clicking the “report an issue“ button at the bottom of the tutorial.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!