mrkeyoor.com_
Tue 22 Sept 22:33 UTC
npmWeb Frontendupdated 22 Sept 2026

formik review

Formik 2.4.9 is a React form-state package built around controlled values and a shared context. We measured 53.3 KB minified and 17.4 KB gzipped for a browser import of the package. In return, Formik keeps values, errors, touched flags, validation and submit state under one API, with Field, FieldArray, Form and hooks for custom controls. The current release fixes JSX ref handling for React 19. It includes TypeScript declarations, although string field paths and several any-typed helper values leave gaps in end-to-end form typing.

Verdict

Formik 2.4.9 added 17.4 KB gzipped in our browser test and left 14 packages on disk, so it is easiest to justify in React apps already organized around its controlled form state. Greenfield forms should compare React Hook Form or TanStack Form before accepting that client cost and the 702-issue backlog.

We installed it

Lab card: what happened when we installed formikScreenshot of formik documentation
Install✓ · 2s14 packages on disk · 11 MB
ImportESM import works · require() works · CommonJS package
Browser17.4 KBgzipped (53.3 KB minified), bundled with esbuild
TypesTypeScript types bundled
Known vulns00 critical · 0 high · 0 moderate · 0 low (npm audit)

Answers from our run

Does formik install cleanly?

Yes. In a fresh container with an empty cache, npm install formik finished in 2 seconds, leaving 14 packages and 11 MB on disk. npm audit reported no known vulnerabilities.

How much does formik add to a browser bundle?

17.4 KB gzipped (53.3 KB minified) when the whole package is bundled for the browser with esbuild. Importing only part of it is usually smaller.

Does formik work with both ESM and CommonJS?

Yes. Both import 'formik' and require('formik') worked in Node 22 in our run. The package is published as CommonJS.

Does formik include TypeScript types?

Yes, type declarations ship inside the package, so no @types install is needed.

formik or react-hook-form: which should you use?

react-hook-form: Choose it for new React forms that benefit from uncontrolled inputs and field-level subscriptions. Formik 2.4.9 added 17.4 KB gzipped in our browser test and left 14 packages on disk, so it is easiest to justify in React apps already organized around its controlled form state.

When should you not use formik?

You are choosing for a new form-heavy app where rerender isolation matters; React Hook Form and TanStack Form offer field-granular models

API stability4/5Formik's version 2 contracts still center on Formik, Form, Field, FieldArray, useField, values, errors, touched and the documented helper methods. The 2.4.9 change was a React 19 JSX ref repair rather than an application-facing redesign. Deprecated render props and the published 3.0.0-next.8 tag keep the score below 5, since old patterns and future migration work remain visible.
Docs4/5formik.org has separate references for components and hooks, plus guides for validation timing, arrays, TypeScript, submission and React Native. The FieldArray page explicitly warns that array errors can be strings or nested arrays, and the useFormik page states which context components stop working with that hook. Some examples still use deprecated render props, and the TypeScript guide does not dwell on untyped string paths.
Maintenance3/5The stable 2.4.9 release arrived on November 10, 2025 with a React 19 ref fix, one patch after the JSX namespace compatibility change. The repository is open and unarchived, yet that release date is also its latest push. GitHub search reports 702 open issues, while the repository API reports 839 combined issues and pull requests, so a narrowly maintained stable line sits behind a large queue.
Ecosystem5/5npm counted 4,623,534 Formik downloads from August 18 through 24, 2026, and GitHub reports 34,331 stars. React 16.8 and later fit the peer declaration, and years of examples cover UI controls, Yup schemas, nested paths and arrays. That installed base makes maintenance and hiring easier for existing Formik apps, even as newer form libraries compete on render behavior and typing.

Use it if

  • You are maintaining a React app whose forms already share Formik 2 components, helpers and validation conventions
  • Your team wants controlled values, touched state, errors and submission status in one inspectable object
  • Your forms use nested paths or repeatable arrays and need the FieldArray operations documented by Formik
  • You need both field-level and whole-form validation, with an optional Yup schema adapter
Skip it if

Setup reality

In our install, Formik 2.4.9 completed in 2 seconds, producing 14 packages and 11 MB on disk. npm audit reported 0 known vulnerabilities. The package has 8 direct dependencies, 1 React peer, an Apache-2.0 license and 624 KB unpacked. It is CommonJS without an exports map, though require() and ESM import both worked. Types are bundled. The full import built to 53.3 KB minified and 17.4 KB gzipped.

React 16.8 or newer satisfies the peer range. Yup is absent from the install, so add it only when using validationSchema. Every controlled field needs an initial value, including empty strings and arrays. Names such as profile.email and friends[0].name drive nested access at runtime; TypeScript does not prove those strings match your Values shape.

Change and blur validation are enabled by default. Expensive validators can run on each edit unless validateOnChange or validateOnBlur is changed. touched depends on forwarding onBlur from Field or useField. FieldArray also validates after array operations by default, and its top-level error can be a string while item errors form an array. Check the value's shape before rendering it.

Formik clears isSubmitting after an async onSubmit promise settles. If the handler returns no promise, finish the cycle with setSubmitting(false). New initialValues do not replace current state unless enableReinitialize is true; resetForm({ values }) can instead establish a new baseline after saving. Version 2.4.9 fixes React 19 JSX ref handling, while npm still exposes a 3.0.0-next prerelease. Keep production code on the stable 2.x contract unless you are testing that migration deliberately.

Patterns

Create a typed Formik form build-typed-form

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

type Values = { email: string }

export function Signup() {
  return (
    <Formik<Values>
      initialValues={{ email: '' }}
      validate={(values) =>
        values.email ? {} : { email: 'Required' }
      }
      onSubmit={async (values) => save(values)}
    >
      {({ errors, touched, isSubmitting }) => (
        <Form>
          <Field name="email" type="email" />
          {touched.email && errors.email && <p>{errors.email}</p>}
          <button disabled={isSubmitting}>Save</button>
        </Form>
      )}
    </Formik>
  )
}

initialValues must include email from the first render. The string passed to Field is not checked as a keyof Values.

Validate with an installed Yup schema use-yup-schema

import * as yup from 'yup'

const schema = yup.object({
  email: yup.string().email('Use a valid email').required('Required'),
})

<Formik
  initialValues={{ email: '' }}
  validationSchema={schema}
  onSubmit={submit}
>
  <Form><Field name="email" /></Form>
</Formik>

Yup is a separate npm dependency. Formik converts Yup errors into an object shaped like values and touched.

Connect a design-system input with useField build-custom-field

function TextInput({ label, ...props }) {
  const [field, meta] = useField(props)
  return (
    <label>
      {label}
      <input {...field} {...props} />
      {meta.touched && meta.error ? <span>{meta.error}</span> : null}
    </label>
  )
}

<TextInput name="displayName" label="Display name" />

Spread field before the final props only when intentional overrides are acceptable. onBlur must reach the input for touched state to update.

Update a field from its previous value update-field-functionally

<button
  type="button"
  onClick={() => setFieldValue('quantity', (current) => current + 1)}
>
  Add one
</button>

Functional setFieldValue support is present in Formik 2.4.7 and later. The field parameter remains a string and the callback value is typed as any.

Defer validation during a burst of updates skip-change-validation

await setFieldValue('country', 'CA', false)
await setFieldValue('region', '', false)
const errors = await validateForm()

The third setFieldValue argument disables validation for that call. validateForm() then checks the resulting form once.

Add and remove rows with FieldArray manage-field-array

<FieldArray name="friends">
  {({ push, remove }) => (
    <>
      {values.friends.map((friend, index) => (
        <div key={friend.id}>
          <Field name={`friends[${index}].name`} />
          <button type="button" onClick={() => remove(index)}>Remove</button>
        </div>
      ))}
      <button type="button" onClick={() => push({ id: crypto.randomUUID(), name: '' })}>Add</button>
    </>
  )}
</FieldArray>

FieldArray operations run validation and update touched by default. Use a stable item id as the React key even though the Formik path uses the current index.

Handle both array and item validation errors render-array-errors

const friendsError = getIn(errors, 'friends')

return typeof friendsError === 'string'
  ? <p>{friendsError}</p>
  : <ErrorMessage name={`friends[${index}].name`} />

A Yup array rule can produce a string, while failed child rules produce a nested array. Rendering the array directly can crash React.

Reset when a loaded record changes reinitialize-record

<Formik
  initialValues={{ title: record.title }}
  enableReinitialize
  onSubmit={saveRecord}
>
  <Form><Field name="title" /></Form>
</Formik>

enableReinitialize uses deep equality on initialValues and resets edits when a different baseline arrives. Avoid feeding it an object that changes accidentally.

Make saved values the new clean state set-new-baseline

onSubmit={async (values, actions) => {
  const saved = await api.save(values)
  actions.resetForm({ values: saved })
}}

resetForm({ values }) changes the initial baseline as well as current values, so dirty becomes false after the save.

Store a form-wide API failure handle-server-error

onSubmit={async (values, actions) => {
  try {
    await api.createAccount(values)
  } catch (error) {
    actions.setStatus({ message: 'Account creation failed' })
  }
}}

{status?.message && <div role="alert">{status.message}</div>}

status accepts any value and has no schema. Field-specific server failures belong in setFieldError when the response identifies a field.

Collect checkbox choices in an array configure-checkbox-group

<Formik initialValues={{ roles: [] }} onSubmit={submit}>
  <Form>
    <label><Field type="checkbox" name="roles" value="editor" /> Editor</label>
    <label><Field type="checkbox" name="roles" value="reviewer" /> Reviewer</label>
  </Form>
</Formik>

Checkboxes sharing a name and carrying distinct value props update an array. initialValues.roles must start as an array.

Build a form directly with useFormik use-formik-without-context

const formik = useFormik({
  initialValues: { query: '' },
  onSubmit: search,
})

return (
  <form onSubmit={formik.handleSubmit}>
    <input name="query" value={formik.values.query} onChange={formik.handleChange} />
  </form>
)

useFormik does not create Formik context. Field, FastField, ErrorMessage and FieldArray cannot connect beneath this form.

Alternatives

PackageRegistryPick it when
react-hook-formnpmChoose it for new React forms that benefit from uncontrolled inputs and field-level subscriptions
@tanstack/react-formnpmChoose it when strong type inference and fine-grained reactive form state justify a newer API
react-final-formnpmChoose it when subscription-based rendering and the separate Final Form state engine fit the application
final-formnpmChoose the framework-neutral core when React components should not own the form model

More web frontend guides

postcss · react · react-dom · tailwindcss · htmlparser2 · tailwind-merge · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.