Formik Library for Form Handling

Loading

Formik is a popular form library for React that helps with form state management, validation, and submission handling. Here’s everything you need to know to use Formik effectively:

Core Concepts

1. Basic Formik Setup

import { Formik, Form, Field, ErrorMessage } from 'formik';

function BasicForm() {
  return (
    <Formik
      initialValues={{ email: '', password: '' }}
      onSubmit={(values, { setSubmitting }) => {
        console.log(values);
        setSubmitting(false);
      }}
    >
      {({ isSubmitting }) => (
        <Form>
          <Field type="email" name="email" />
          <ErrorMessage name="email" component="div" />

          <Field type="password" name="password" />
          <ErrorMessage name="password" component="div" />

          <button type="submit" disabled={isSubmitting}>
            Submit
          </button>
        </Form>
      )}
    </Formik>
  );
}

2. Validation with Yup

import * as Yup from 'yup';

const validationSchema = Yup.object().shape({
  email: Yup.string()
    .email('Invalid email')
    .required('Required'),
  password: Yup.string()
    .min(8, 'Too short!')
    .required('Required')
});

function ValidationForm() {
  return (
    <Formik
      initialValues={{ email: '', password: '' }}
      validationSchema={validationSchema}
      onSubmit={(values) => console.log(values)}
    >
      {({ isSubmitting }) => (
        <Form>
          <Field type="email" name="email" placeholder="Email" />
          <ErrorMessage name="email" component="div" className="error" />

          <Field type="password" name="password" placeholder="Password" />
          <ErrorMessage name="password" component="div" className="error" />

          <button type="submit" disabled={isSubmitting}>
            Submit
          </button>
        </Form>
      )}
    </Formik>
  );
}

Advanced Features

1. Handling Form State

function StatefulForm() {
  return (
    <Formik
      initialValues={{ username: '', rememberMe: false }}
      onSubmit={(values) => console.log(values)}
    >
      {({ values, handleChange, handleBlur }) => (
        <Form>
          <Field 
            name="username" 
            onChange={handleChange}
            onBlur={handleBlur}
            value={values.username}
          />

          <label>
            <Field type="checkbox" name="rememberMe" />
            Remember me
          </label>

          <pre>{JSON.stringify(values, null, 2)}</pre>
        </Form>
      )}
    </Formik>
  );
}

2. Dynamic Fields

function DynamicForm() {
  return (
    <Formik
      initialValues={{ friends: [''] }}
      onSubmit={(values) => console.log(values)}
    >
      {({ values }) => (
        <Form>
          {values.friends.map((friend, index) => (
            <div key={index}>
              <Field name={`friends.${index}`} />
              <button
                type="button"
                onClick={() => arrayHelpers.remove(index)}
              >
                Remove
              </button>
            </div>
          ))}
          <button
            type="button"
            onClick={() => arrayHelpers.push('')}
          >
            Add Friend
          </button>
          <button type="submit">Submit</button>
        </Form>
      )}
    </Formik>
  );
}

3. Custom Field Components

function CustomInput({ field, form, ...props }) {
  return (
    <div>
      <input {...field} {...props} />
      {form.touched[field.name] && form.errors[field.name] && (
        <div className="error">{form.errors[field.name]}</div>
      )}
    </div>
  );
}

function CustomFieldForm() {
  return (
    <Formik
      initialValues={{ email: '' }}
      onSubmit={(values) => console.log(values)}
    >
      <Form>
        <Field name="email" component={CustomInput} placeholder="Email" />
        <button type="submit">Submit</button>
      </Form>
    </Formik>
  );
}

Integration Patterns

1. With UI Libraries (Material-UI Example)

import { TextField, Checkbox, FormControlLabel } from '@material-ui/core';

function MuiForm() {
  return (
    <Formik
      initialValues={{ name: '', agree: false }}
      onSubmit={(values) => console.log(values)}
    >
      {({ values, handleChange, handleBlur }) => (
        <Form>
          <TextField
            name="name"
            label="Name"
            value={values.name}
            onChange={handleChange}
            onBlur={handleBlur}
            fullWidth
          />

          <FormControlLabel
            control={
              <Checkbox
                name="agree"
                checked={values.agree}
                onChange={handleChange}
              />
            }
            label="I agree to terms"
          />

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

2. With React Context

const FormContext = React.createContext();

function FormProvider({ children }) {
  const formik = useFormik({
    initialValues: { theme: 'light' },
    onSubmit: values => console.log(values)
  });

  return (
    <FormContext.Provider value={formik}>
      <Form>{children}</Form>
    </FormContext.Provider>
  );
}

function ThemedForm() {
  const { values, handleChange } = React.useContext(FormContext);

  return (
    <>
      <select
        name="theme"
        value={values.theme}
        onChange={handleChange}
      >
        <option value="light">Light</option>
        <option value="dark">Dark</option>
      </select>
    </>
  );
}

Best Practices

  1. Initial Values: Always provide complete initial values
   initialValues={{
     username: '',
     email: '',
     address: {
       street: '',
       city: ''
     }
   }}
  1. Validation Strategies:
  • Use Yup for complex validation
  • For simple validation, use validate function:
    jsx validate={values => { const errors = {}; if (!values.email) errors.email = 'Required'; return errors; }}
  1. Form Submission:
   onSubmit={(values, { setSubmitting, resetForm }) => {
     submitToAPI(values)
       .then(() => {
         setSubmitting(false);
         resetForm();
       })
       .catch(error => {
         setSubmitting(false);
         setErrors({ form: error.message });
       });
   }}
  1. Performance Optimization:
  • Use React.memo for custom field components
  • Split large forms into smaller components
  • Use shouldValidate and shouldReset props carefully
  1. Accessibility:
  • Always associate labels with inputs
  • Provide proper error messages
  • Use ARIA attributes where appropriate

Comparison with React Hook Form

While both libraries solve similar problems, here’s when to choose Formik:

  1. Choose Formik when:
  • You prefer a more declarative API
  • You need built-in form state management
  • You’re already using Yup for validation
  • You work with complex nested forms
  1. Choose React Hook Form when:
  • Performance is critical (fewer re-renders)
  • You prefer uncontrolled components
  • You need minimal bundle size
  • You work with very large forms

Formik provides excellent developer experience and remains a great choice for many React applications, especially those that benefit from its structured approach to form management.

Leave a Reply

Your email address will not be published. Required fields are marked *