Formik

repository·main·Indexed 12 days ago

https://github.com/jaredpalmer/formik

A React library designed to simplify building, managing, and validating forms. It handles form state, validation, and submission, providing components like <Field />, <FastField /> for performance optimization, and <ErrorMessage /> for field-level error reporting. It also includes a connect() HoC for injecting Formik context into custom components and supports integration with 3rd party UI frameworks such as Material UI, Ant Design, and Bootstrap.

Tokens
44.2K
Snippets
101
Records
145
Agent score
95%

What's inside Formik

  1. The Formik documentation technology stack

    main

    The Formik documentation website is built using a modern web stack designed for high performance and developer experience. The core components include:

    • Next.js (9.4.x): Used as the primary framework to leverage features like getStaticProps, catch-all routes, and incremental static-site generation (ISG).
    • MDX: Enables writing documentation using Markdown with embedded React components.
    • Tailwind CSS: An Atomic CSS framework used for styling.
    • Notion: Powers the blog content via the Notion API.
    • Algolia DocSearch (v3 Alpha): Provides the search functionality, including an omnibar and search history.

    This stack was chosen over Gatsby and Docusaurus v2 to allow for more client-side interactivity (like in-page playgrounds) and easier integration with existing React-based design systems.

  2. What is Formik?

    main

    Formik is a library consisting of React components and hooks designed for building forms in React and React Native. It centralizes the management of three core form concerns:

    1. Form State: Getting values in and out of the form state.
    2. Validation: Managing validation logic and displaying error messages.
    3. Submission: Handling form submission processes.

    By colocating these concerns, Formik simplifies testing, refactoring, and reasoning about form logic.

  3. Use the <Field /> component to connect inputs to Formik

    main

    The <Field /> component automatically hooks up inputs to Formik state using the name attribute. By default, it renders an HTML <input /> element. You can customize how it renders using the as, children, or component props.

    import { Field, Form, Formik } from 'formik';
    
    const Example = () => (
      <Formik
        initialValues={{ email: '' }}
        onSubmit={values => console.log(values)}
      >
        <Form>
          <Field name="email" type="email" placeholder="Email" />
          <button type="submit">Submit</button>
        </Form>
      </Formik>
    );
  4. Track visited fields using `touched` and `handleBlur`

    main

    To improve user experience, you should avoid showing validation errors for fields the user hasn't interacted with yet. Formik provides a touched object that mirrors the shape of your values. It contains boolean values indicating whether a field has been visited.

    To populate the touched state, pass formik.handleBlur to the onBlur prop of your input elements. This allows you to conditionally render error messages by checking both formik.touched.fieldName and formik.errors.fieldName.

    <input
      id="firstName"
      name="firstName"
      type="text"
      onChange={formik.handleChange}
      onBlur={formik.handleBlur}
      value={formik.values.firstName}
    />
    {formik.touched.firstName && formik.errors.firstName ? (
      <div>{formik.errors.firstName}</div>
    ) : null}
  5. How withFormik injects props and methods

    main

    When using withFormik, the wrapped component receives a set of injected props and methods. These are identical to the props provided by the <Formik /> component. They include:

    • values: The current form values.
    • touched: An object representing which fields have been visited.
    • errors: An object containing validation errors.
    • status: Arbitrary state that can be updated via setStatus.
    • isSubmitting: A boolean indicating if the form is currently submitting.
    • isValid: A boolean indicating if the form is valid.
    • handleChange: Handler for onChange events.
    • handleBlur: Handler for onBlur events.
    • handleSubmit: Handler for onSubmit events.
    • resetForm: Method to reset the form to initial values.
    • setFieldValue, setFieldTouched, setFieldError, setErrors, setTouched, setValues, setStatus, setSubmitting: Methods to manually update form state.
  6. Breaking Change: `onChange` and `onBlur` behavior in v3

    main

    In Formik v3, the onChange and onBlur handlers returned from getFieldProps, useField(), or the <Field> render prop have changed.

    The Change: Previously, these methods were identical to handleChange and handleBlur, meaning they could be curried (e.g., onChange(name)(event)). In v3, they are already scoped to the specific field.

    New Behavior:

    • They can now accept either a React Synthetic event OR an arbitrary value directly.
    • They cannot be curried.

    Comparison:

    Incorrect (Curried style - no longer works):

    field.onChange(props.name)('bar');

    Correct (Direct value or event):

    // Pass a value directly
    field.onChange('bar');
    
    // Or pass the event
    field.onChange(e);

    Note: If you use handleChange or handleBlur directly from the Formik render props (the ones returned by useFormik), they still support currying.

    // This still works because it uses the top-level handleChange, not the field-scoped one
    <Formik
      initialValues={{ email: '' }}
      onSubmit={values => console.log(values)}
    >
      {({ handleChange, handleBlur, handleSubmit, values }) => (
        <TextInput
          onChangeText={handleChange('email')} // curried
          onBlur={handleBlur('email')} // curried
          value={values.email}
        />
      )}
    </Formik>
  7. Use the useField hook for custom field components

    main

    useField is a React hook used to thread Formik behaviors into arbitrary field components. It provides more flexibility than the <Field> component, especially when you need to build custom UI that doesn't map directly to a standard HTML input.

    You can use it in two ways:

    1. Passing a string: const [field, meta, helpers] = useField('fieldName'); (returns basic input props).
    2. Passing a config object: const [field, meta, helpers] = useField(props); (mimics <Field> behavior, allowing you to leverage Formik's specialized logic for checkbox, radio, or multiple select by including those keys in the object).

    This is particularly useful for components that aren't inputs (like a custom button group) where you want to use meta.value to track state and helpers.setValue to update it imperatively.

    import React from 'react';
    import { useField, Form, Formik } from 'formik';
    
    interface Values {
      firstName: string;
      lastName: string;
      email: string;
    }
    
    const MyTextField = ({ label, ...props }) => {
      // Passing the whole props object allows useField to pick up 'name', 'type', etc.
      const [field, meta] = useField(props);
      return (
        <>
          <label>
            {label}
            <input {...field} {...props} />
          </label>
          {meta.touched && meta.error ? (
            <div className="error">{meta.error}</div>
          ) : null}
        </>
      );
    };
    
    const Example = () => (
      <Formik
        initialValues={{ email: '', firstName: 'red', lastName: '' }}
        onSubmit={(values) => alert(JSON.stringify(values, null, 2))}
      >
        {(props) => (
          <Form>
            <MyTextField name="firstName" type="text" label="First Name" />
            <button type="submit">Submit</button>
          </Form>
        )}
      </Formik>
    );
  8. Understand the Formik submission lifecycle

    main

    Formik follows a specific sequence of phases when a submission is triggered via handleSubmit(e) or submitForm. Understanding these phases helps in managing UI states like loading spinners or error visibility.

    1. Pre-submit Phase

    • All fields are marked as touched (this ensures validation errors are visible in the UI).
    • isSubmitting is set to true.
    • submitCount is incremented.
    • Note: initialValues must be provided for this to work correctly.

    2. Validation Phase

    • isValidating is set to true.
    • Formik runs field-level validations, the validate function, and validationSchema (if provided).
    • If errors exist: Submission is aborted. isValidating and isSubmitting are set to false, and the errors object is populated.
    • If no errors exist: isValidating is set to false, and the process moves to the Submission phase.

    3. Submission Phase

    • The submission handler (onSubmit) is executed.
    • If the handler returns a Promise: Formik waits for the promise to resolve or reject, then automatically sets isSubmitting to false.
    • If the handler does NOT return a Promise: You must manually call setSubmitting(false) to complete the cycle.
  9. When to use the useFormik hook

    main

    useFormik() is a custom React hook that returns all Formik state and helpers directly.

    Important Constraints

    Do not use useFormik() if you intend to use Formik's context-based components. Because useFormik() does not create a React Context Provider, the following components will NOT work with it:

    • <Field>
    • <FastField>
    • <ErrorMessage>
    • <FieldArray>
    • connect()

    If you are already using the <Formik> component and need to access state in a child component, use useFormikContext instead.

    • You want to avoid using React Context (e.g., for performance reasons).
    • You are building a custom version of the <Formik> component.
    • You are managing form state entirely manually without a Provider wrapper.
  10. When to use <FastField /> for performance optimization

    main

    <FastField /> is an optimized version of <Field /> designed to reduce unnecessary re-renders in large forms (typically ~30+ fields) or for fields with expensive validation requirements. It has the exact same API as <Field /> but implements shouldComponentUpdate() internally to block re-renders unless the specific slice of Formik state relevant to that field changes.

    When to use it

    You can use <FastField /> as a drop-in replacement for <Field /> if the field is "independent." A field is considered independent if:

    1. It does not change behavior or render anything based on updates to another <Field /> or <FastField />'s slice of state.
    2. It does not rely on top-level <Formik /> state properties like isValidating or submitCount.

    Re-render triggers

    A <FastField name="firstName" /> will only re-render when:

    • values.firstName, errors.firstName, touched.firstName, or isSubmitting change (via shallow comparison; dotpaths are supported).
    • A prop is added or removed from the <FastField />.
    • The name prop changes.

    Warning: If you use <FastField />, any attempt to access state from other fields via the form prop (e.g., form.values.otherField) inside the <FastField /> render function will not trigger a re-render when otherField changes.

    import React from 'react';
    import { Formik, Field, FastField, Form } from 'formik';
    import * as Yup from 'yup';
    
    const Basic = () => (
      <Formik
        initialValues={{ firstName: '', lastName: '', email: '' }}
        onSubmit={values => console.log(values)}
      >
        {formikProps => (
          <Form>
            {/* This only updates for changes to firstName slice */}
            <FastField name="firstName" />
    
            {/* This updates for ALL changes because it uses top-level formikProps */}
            {formikProps.touched.firstName && formikProps.errors.firstName && (
              <div>{formikProps.errors.firstName}</div>
            )}
          </Form>
        )}
      </Formik>
    );
  11. How Formik's React Context-powered components work

    main

    Formik provides a set of components—<Formik />, <Form />, <Field />, and <ErrorMessage />—that use React Context to implicitly connect to the form state and helpers.

    To use these components, you must render a <Formik> component (which acts as a Context Provider) at the top of your form tree. The <Formik> component can take a function as its children (a render prop), which receives the exact same object returned by the useFormik() hook.

    import React from 'react';
    import { Formik } from 'formik';
    
    const SignupForm = () => {
      return (
        <Formik
          initialValues={{ firstName: '', email: '' }}
          onSubmit={(values, { setSubmitting }) => {
            setTimeout(() => {
              alert(JSON.stringify(values, null, 2));
              setSubmitting(false);
            }, 400);
          }}
        >
          {formik => (
            <form onSubmit={formik.handleSubmit}>
              <input {...formik.getFieldProps('firstName')} />
              <button type="submit">Submit</button>
            </form>
          )}
        </Formik>
      );
    };
  12. Set field values based on other fields

    main

    In Formik, you can implement dependent fields (where the value of one field changes automatically based on the value of another) by using the setFieldValue method provided by the Formik render props or the useFormikContext hook.

    Typically, you monitor changes to a 'source' field using an onChange handler or a useEffect hook, and then call setFieldValue('targetField', newValue) to update the dependent field.

    // Conceptual pattern for dependent fields
    <Formik
      initialValues={{ source: '', target: '' }}
      onSubmit={values => console.log(values)}
    >
      {({ setFieldValue }) => (
        <Form>
          <Field
            name="source"
            onChange={(e) => {
              const value = e.target.value;
              // Update the source field manually if needed, or let Field handle it
              // Then update the dependent field
              setFieldValue('source', value);
              setFieldValue('target', `Derived from ${value}`);
            }}
          />
          <Field name="target" />
        </Form>
      )}
    </Formik>