Tutorial

Tear-Free Forms with React and Formik

Published on October 19, 2018
author

joshtronic

Tear-Free Forms with React and Formik

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”.

Getting Started

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 Formik Component

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.

The Form Component

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>

The State of the Inputs

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.

Bringing It All Together

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}
      />
      &nbsp;
      <input
        type="reset"
        value="Reset"
        onClick={props.handleReset}
        disabled={!props.dirty || props.isSubmitting}
      />
    </Form>
  )}
</Formik>

Conclusion

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.

Learn more about our products

About the authors
Default avatar
joshtronic

author

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.

Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
Leave a comment


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!

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Become a contributor for community

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

DigitalOcean Documentation

Full documentation for every DigitalOcean product.

Resources for startups and SMBs

The Wave has everything you need to know about building a business, from raising funding to marketing your product.

Get our newsletter

Stay up to date by signing up for DigitalOcean’s Infrastructure as a Newsletter.

New accounts only. By submitting your email you agree to our Privacy Policy

The developer cloud

Scale up as you grow — whether you're running one virtual machine or ten thousand.

Get started for free

Sign up and get $200 in credit for your first 60 days with DigitalOcean.*

*This promotional offer applies to new accounts only.