In Formik 2, what do <Formik>, <Form>, <Field> and <ErrorMessage> each wire up, and when would you call useFormik directly instead?
answer
- one provider, three consumers
- value and handlers from context
- error shown only when touched
- the hook has no context
basics
~20 sIn Formik 2, <Formik> owns the form state and provides it through context; <Form> wires submit and reset; <Field> binds an input to values, handleChange and handleBlur; <ErrorMessage> shows a touched field's error. useFormik returns the same state without context.
solid answer
~40 s`<Formik initialValues onSubmit validationSchema>` creates the form state — `values`, `errors`, `touched`, `isSubmitting` — and puts it in React context, also passing it to a render-prop child. `<Form>` is a `<form>` whose `onSubmit` and `onReset` are already `handleSubmit` and `handleReset`. `<Field name="email" />` renders an `<input>` (or whatever `as`/`component` says) with `name`, `value` from `values.email`, and `onChange`/`onBlur` set to `handleChange`/`handleBlur`. `<ErrorMessage name="email" />` renders `errors.email` only when `touched.email` is also set. `useFormik(config)` returns the same state and helpers **without** a context provider, so `<Field>`, `<FastField>`, `<ErrorMessage>`, `<FieldArray>` and `connect()` do not work with it; the docs say to use it only when you are not using `<Formik>`, wiring inputs yourself with `formik.getFieldProps('email')`.
code
tsx · 29 linesimport { ErrorMessage, Field, Form, Formik } from 'formik';
import * as Yup from 'yup';
const schema = Yup.object({
email: Yup.string().email('Invalid email').required('Required'),
plan: Yup.string().required('Pick a plan'),
});
export function NewsletterForm({ subscribe }: { subscribe: (v: { email: string; plan: string }) => Promise<void> }) {
return (
<Formik initialValues={{ email: '', plan: '' }} validationSchema={schema} onSubmit={subscribe}>
{({ isSubmitting }) => (
<Form>
<Field name="email" type="email" />
<ErrorMessage name="email" component="p" />
<Field name="plan" as="select">
<option value="">Choose...</option>
<option value="weekly">Weekly</option>
<option value="monthly">Monthly</option>
</Field>
<ErrorMessage name="plan" component="p" />
<button type="submit" disabled={isSubmitting}>Subscribe</button>
</Form>
)}
</Formik>
);
}go deeper
Build a small form with <Formik>, <Form>, <Field> and <ErrorMessage> from memory and name what each one wires.
Explain the context split between owner and consumers, and why useFormik breaks every context-based component.
Choose between <Formik> with reusable field components and useFormik for a one-off form, and keep initialValues complete.
Decide whether a codebase's forms standardise on Formik's component API and how shared field components consume its context.
## The pieces and what each one owns Formik 2 splits a form into one **owner** and several **consumers**. The owner holds state; the consumers read it and report events back. | Piece | Role | What it wires | |---|---|---| | `<Formik>` | Owner | Creates `values`, `errors`, `touched`, `isSubmitting`, `submitCount`; provides them via context and to a render-prop child | | `<Form>` | Consumer | A `<form>` with `onSubmit={handleSubmit}` and `onReset={handleReset}` | | `<Field>` | Consumer | An input bound to `values[name]`, `handleChange`, `handleBlur` | | `<ErrorMessage>` | Consumer | Renders `errors[name]` only when `touched[name]` is set | | `useFormik()` | Owner without context | Returns the same state and helpers as a plain object | ## `<Formik>`: the owner `<Formik>` takes the form's configuration: - `initialValues` — required; the starting value for every field, and the shape Formik uses for `touched` on submit. - `onSubmit(values, formikBag)` — called after validation passes. - `validate` or `validationSchema` — a function returning an errors object, or a Yup schema. - `validateOnChange`, `validateOnBlur` (both default `true`), `validateOnMount` (default `false`), `enableReinitialize` (default `false`). Its child can be a function receiving the whole bag — `{ values, errors, touched, handleSubmit, isSubmitting, ... }` — or ordinary elements that read the bag through context. ## `<Form>` and `<Field>`: wiring inputs `<Form>` saves you writing `<form onSubmit={formik.handleSubmit}>`. `<Field name="email" />` saves you writing: ```tsx <input name="email" value={formik.values.email} onChange={formik.handleChange} onBlur={formik.handleBlur} /> ``` `handleChange` finds the field to update from the input's **`name`** attribute (falling back to `id`), which is why every input needs one. It also converts `type="number"` and `type="range"` values with `parseFloat` and handles checkboxes and multi-selects. `<Field>` can render something else with `as="select"`, `as="textarea"` or `as={MyInput}`, or accept a child function that receives `{ field, form, meta }`. ## `<ErrorMessage>`: the touched gate `<ErrorMessage name="email" />` renders nothing unless **both** `touched.email` and `errors.email` are set. That is Formik's built-in answer to "don't shout at users about fields they have not reached yet": the error may already exist in `errors`, but it shows only once the field has been blurred or the form submitted. Use `component="p"` to choose the wrapper, or a child function to format the message. ## `useFormik`: when and why not `useFormik(config)` is the hook `<Formik>` is built on. It returns the same values and helpers, but it **does not render a context provider**. The documentation is explicit: 1. It is "not meant for the majority of use cases". 2. `<Field>`, `<FastField>`, `<ErrorMessage>`, `connect()` and `<FieldArray>` will **not** work with it, because they all read context. 3. If you want the state through context in a child component, use `useFormikContext()` under a `<Formik>` instead. So `useFormik` suits a small, single-component form where you wire inputs by hand, typically with `formik.getFieldProps('email')`, which returns `{ name, value, onChange, onBlur }`. As soon as you want reusable field components, use `<Formik>`. ## Common mistakes - **Omitting `name` on an input** wired to `handleChange`: Formik cannot tell which value to update. - **Using `<ErrorMessage>` with `useFormik`** and wondering why nothing renders. - **Leaving fields out of `initialValues`**: they start as `undefined`, which can make React warn about an uncontrolled input becoming controlled, and they are not marked touched on submit. - **Expecting `<ErrorMessage>` to show errors immediately** after typing, before the field has been touched.
- In Formik 2, how does a deeply nested child component read the form's values without prop drilling?Call `useFormikContext()` in the child. It reads the context that `<Formik>` provides and returns the same bag — `values`, `errors`, `setFieldValue` and so on. It only works under `<Formik>` or `withFormik`, not under a form built with `useFormik()`, which provides no context.
- What does getFieldProps return in Formik 2, and why is it useful with useFormik?`formik.getFieldProps('email')` returns `{ name, value, onChange, onBlur }` for that field — exactly what `<Field>` would inject. Spreading it onto an `<input>` gives you `<Field>`-style wiring in a `useFormik` form, where `<Field>` itself cannot be used because there is no context.
saying these in an interview costs you the question
- <Field> renders an uncontrolled input that Formik reads on submit
- useFormik and <Formik> are interchangeable, so <ErrorMessage> works with both
- <ErrorMessage> shows the error as soon as validation produces it
- handleChange works without a name or id on the input
- initialValues is optional; Formik infers fields from the inputs it sees