formik

$npx mdskill add TerminalSkills/skills/formik

Build and validate React forms with Formik for dynamic and multi-step interfaces

  • Simplifies complex form creation with validation, error handling, and dynamic fields
  • Uses Yup or Zod for schema validation and integrates with React 16+
  • Tracks form state including values, errors, and submission status automatically
  • Provides reusable components like Form, Field, and ErrorMessage for streamlined UI

SKILL.md

.github/skills/formikView on GitHub ↗
---
name: formik
description: >-
  Build forms in React with Formik. Use when creating complex forms with
  validation, multi-step forms, dynamic form fields, or handling form
  submission with error states.
license: Apache-2.0
compatibility: 'React 16+'
metadata:
  author: terminal-skills
  version: 1.0.0
  category: development
  tags: [formik, forms, react, validation, ui]
---

# Formik

## Overview

Formik manages form state in React — values, errors, touched fields, submission. Integrates with Yup/Zod for schema validation. Handles complex forms (multi-step, dynamic fields, arrays) without Redux or complex state management.

## Instructions

### Step 1: Basic Form

```tsx
import { Formik, Form, Field, ErrorMessage } from 'formik'
import * as Yup from 'yup'

const SignupSchema = Yup.object({
  name: Yup.string().min(2).required('Name is required'),
  email: Yup.string().email('Invalid email').required('Email is required'),
  password: Yup.string().min(8, 'At least 8 characters').required('Password is required'),
})

function SignupForm() {
  return (
    <Formik
      initialValues={{ name: '', email: '', password: '' }}
      validationSchema={SignupSchema}
      onSubmit={async (values, { setSubmitting, setErrors }) => {
        try {
          await api.signup(values)
        } catch (err) {
          setErrors({ email: 'Email already registered' })
        } finally {
          setSubmitting(false)
        }
      }}
    >
      {({ isSubmitting }) => (
        <Form>
          <Field name="name" placeholder="Name" />
          <ErrorMessage name="name" component="span" className="error" />

          <Field name="email" type="email" placeholder="Email" />
          <ErrorMessage name="email" component="span" className="error" />

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

          <button type="submit" disabled={isSubmitting}>Sign Up</button>
        </Form>
      )}
    </Formik>
  )
}
```

### Step 2: Dynamic Field Arrays

```tsx
import { FieldArray } from 'formik'

function TeamForm() {
  return (
    <Formik initialValues={{ members: [{ name: '', role: '' }] }} onSubmit={handleSubmit}>
      {({ values }) => (
        <Form>
          <FieldArray name="members">
            {({ push, remove }) => (
              <>
                {values.members.map((_, i) => (
                  <div key={i}>
                    <Field name={`members.${i}.name`} placeholder="Name" />
                    <Field name={`members.${i}.role`} as="select">
                      <option value="">Select role</option>
                      <option value="admin">Admin</option>
                      <option value="member">Member</option>
                    </Field>
                    <button type="button" onClick={() => remove(i)}>Remove</button>
                  </div>
                ))}
                <button type="button" onClick={() => push({ name: '', role: '' })}>
                  Add Member
                </button>
              </>
            )}
          </FieldArray>
        </Form>
      )}
    </Formik>
  )
}
```

## Guidelines

- For new projects, consider react-hook-form (less re-renders). Formik is still solid for existing projects.
- Use schema validation (Yup/Zod) instead of manual validate functions.
- `setErrors` in onSubmit handles server-side validation errors (duplicate email, etc.).
- `<ErrorMessage>` only shows after field is touched — good UX by default.
- For large forms, use `enableReinitialize` when initial values come from API.

More from TerminalSkills/skills