Formik is a library that makes form handling in React apps easy.
In this article, we’ll look at how to handle form inputs with Formik
Dependent Fields
We can set one field’s value based on a value of another field.
For instance, we can write:
import React from "react";
import { Formik, Field, Form, useField, useFormikContext } from "formik";
const MyField = (props) => {
const {
values: { textA },
touched,
setFieldValue
} = useFormikContext();
const [field, meta] = useField(props);
React.useEffect(() => {
if (textA.trim() !== "" && touched.textA) {
setFieldValue(props.name, `textA: ${textA}`);
}
}, [textA, touched.textA, setFieldValue, props.name]);
return (
<>
<input {...props} {...field} />
{!!meta.touched && !!meta.error && <div>{meta.error}</div>}
</>
);
};
const initialValues = { textA: "", textB: "" };
export const FormExample = () => (
<div>
<Formik
initialValues={initialValues}
onSubmit={async (values) => alert(JSON.stringify(values, null, 2))}
>
<Form>
<label>
textA
<Field name="textA" />
</label>
<label>
textB
<MyField name="textB" />
</label>
<button type="submit">Submit</button>
</Form>
</Formik>
</div>
);
export default function App() {
return (
<div className="App">
<FormExample />
</div>
);
}
We created the MyField component and use the useFormikContext component to get the status of the textA field.
We get its value from the values.textA property.
touched has the touched value.
setFieldValue lets us set the field value of another field.
We watch the value the textA field with the useEffect callback and the array with the textA field value, the touched.textA property which has the touched status of textA , setFieldValue , and props.name .
We watch all of them for changes.
Then in FormExample , we use MyField to create the textB field.
It watches the textA field for changes, so when we enter something into the textA field, we see the text of the textB set with the value of textA .
Object Array Fields
We can bind fields to an array of objects.
To do this, we write:
import React from "react";
import { Formik, Field, Form, FieldArray, ErrorMessage } from "formik";
const initialValues = {
friends: [
{
name: "",
email: ""
}
]
};
export const FormExample = () => (
<div>
<Formik
initialValues={initialValues}
onSubmit={async (values) => {
await new Promise((r) => setTimeout(r, 500));
alert(JSON.stringify(values, null, 2));
}}
>
{({ values }) => (
<Form>
<FieldArray name="friends">
{({ remove, push }) => (
<div>
{values.friends.length > 0 &&
values.friends.map((friend, index) => (
<div className="row" key={index}>
<div className="col">
<label htmlFor={`friends.${index}.name`}>Name</label>
<Field name={`friends.${index}.name`} type="text" />
<ErrorMessage
name={`friends.${index}.name`}
component="div"
className="field-error"
/>
</div>
<div className="col">
<label htmlFor={`friends.${index}.email`}>Email</label>
<Field name={`friends.${index}.email`} type="email" />
<ErrorMessage
name={`friends.${index}.name`}
component="div"
className="field-error"
/>
</div>
<div className="col">
<button
type="button"
className="secondary"
onClick={() => remove(index)}
>
X
</button>
</div>
</div>
))}
<button
type="button"
className="secondary"
onClick={() => push({ name: "", email: "" })}
>
Add
</button>
</div>
)}
</FieldArray>
<button type="submit">Invite</button>
</Form>
)}
</Formik>
</div>
);
export default function App() {
return (
<div className="App">
<FormExample />
</div>
);
}
We just set the name to the path of the property we want to bind to in the Field component.
Likewise, we do the same with the ErrorMessage field to get the form validation error messages for the field with the given property path.
To remove an entry, we can use the remove function that comes with Formik.
Conclusion
We can add dependant fields and bind fields to an array of objects with Formik.