Formik

created:

updated:

tags: frontend react formik

What is Formik?

  • A small group of React components and hooks for building forms in React and React Native. It helps with three parts:
    • Getting values in and out of form state
    • Validation and error messages
    • Handling form submission

Formik helps keeping things organized and making testing, refactoring, and reasoning about form much easier.

Example

The following code snippet is from Formik’s official tutorial.

import React from 'react';
import { useFormik } from 'formik';

// A custom validation function. This must return an object
// which keys are symmetrical to our values/initialValues
const validate = values => {
  const errors = {};
  if (!values.firstName) {
    errors.firstName = 'Required';
  } else if (values.firstName.length > 15) {
    errors.firstName = 'Must be 15 characters or less';
  }

  if (!values.lastName) {
    errors.lastName = 'Required';
  } else if (values.lastName.length > 20) {
    errors.lastName = 'Must be 20 characters or less';
  }

  if (!values.email) {
    errors.email = 'Required';
  } else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(values.email)) {
    errors.email = 'Invalid email address';
  }

  return errors;
};

const SignupForm = () => {
  // Pass the useFormik() hook initial form values, a validate function that will be called when
  // form values change or fields are blurred, and a submit function that will
  // be called when the form is submitted
  const formik = useFormik({
    initialValues: {
      firstName: '',
      lastName: '',
      email: '',
    },
    validate,
    onSubmit: values => {
      alert(JSON.stringify(values, null, 2));
    },
  });
  return (
    <form onSubmit={formik.handleSubmit}>
      <label htmlFor="firstName">First Name</label>
      <input
        id="firstName"
        name="firstName"
        type="text"
        onChange={formik.handleChange}
        value={formik.values.firstName}
      />
      {formik.errors.firstName ? <div>{formik.errors.firstName}</div> : null}

      <label htmlFor="lastName">Last Name</label>
      <input
        id="lastName"
        name="lastName"
        type="text"
        onChange={formik.handleChange}
        value={formik.values.lastName}
      />
      {formik.errors.lastName ? <div>{formik.errors.lastName}</div> : null}

      <label htmlFor="email">Email Address</label>
      <input
        id="email"
        name="email"
        type="email"
        onChange={formik.handleChange}
        value={formik.values.email}
      />
      {formik.errors.email ? <div>{formik.errors.email}</div> : null}

      <button type="submit">Submit</button>
    </form>
  );
};

formik returned from useFormik() provides form’s states and helper methods such as:

  • handleSubmit: a submission handler
  • handleChange: a change handler to pass to each <input>, <select> or <textarea>
  • values: form’s current values

Notes

  • We pass id and name that matches the property we defined in initialValues

Validation

Formik keeps track of form’s values as well as its validation and error messages. In order to add validation with JavaScript, we can specify a custom validation function and pass it as validate to the useFormik() hook. If an error occurs, the custom validation function should produce an error object matching our values/initialValues.

  • formik.errors is populated via the custom validation function. By default, Formik will validate after each keystroke, each input’s blur event, prior to submission.
  • The onSubmit function will be executed only if there are no errors

Visited fields

Most of the time, we only want to show a field’s error message after our user is done typing in that field. Similar to errors and values, Formik keeps track of which fields have been visited, in an object called touched.

  • The keys of touched are field names, and the values of touched are booleans true/false.
  • We pass formik.handleBlur to each input’s onBlur prop to take advantage of touched
    • Now that we’re tracking touched, we can change error message rendering logic to only show a given field’s error message if it exists and if the user has visited that field

Reducing boilerplate

  • <Field>: render an <input> component that will grab the respective onChange, onBlur, value props given a name prop and pass them to the element as well as any props you pass to it.
  • useField: we can compose reusable input privmitive components that we can share around our application.
    const MyTextInput = ({ label, ...props }) => {
    // useField() returns [formik.getFieldProps(), formik.getFieldMeta()]
    // which we can spread on <input>. We can use field meta to show an error
    // message if the field is invalid and it has been touched (i.e. visited)
    const [field, meta] = useField(props);
    return (
      <>
        <label htmlFor={props.id || props.name}>{label}</label>
        <input className="text-input" {...field} {...props} />
        {meta.touched && meta.error ? (
          <div className="error">{meta.error}</div>
        ) : null}
      </>
      );
    };
    

References