What is Formik?
- A small group of React components and hooks for building forms in React and React Native. It helps with three parts:
- Getting values in and out of form state
- Validation and error messages
- Handling form submission
Formik helps keeping things organized and making testing, refactoring, and reasoning about form much easier.
Example
The following code snippet is from Formik’s official tutorial.
import React from 'react';
import { useFormik } from 'formik';
// A custom validation function. This must return an object
// which keys are symmetrical to our values/initialValues
const validate = values => {
const errors = {};
if (!values.firstName) {
errors.firstName = 'Required';
} else if (values.firstName.length > 15) {
errors.firstName = 'Must be 15 characters or less';
}
if (!values.lastName) {
errors.lastName = 'Required';
} else if (values.lastName.length > 20) {
errors.lastName = 'Must be 20 characters or less';
}
if (!values.email) {
errors.email = 'Required';
} else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(values.email)) {
errors.email = 'Invalid email address';
}
return errors;
};
const SignupForm = () => {
// Pass the useFormik() hook initial form values, a validate function that will be called when
// form values change or fields are blurred, and a submit function that will
// be called when the form is submitted
const formik = useFormik({
initialValues: {
firstName: '',
lastName: '',
email: '',
},
validate,
onSubmit: values => {
alert(JSON.stringify(values, null, 2));
},
});
return (
<form onSubmit={formik.handleSubmit}>
<label htmlFor="firstName">First Name</label>
<input
id="firstName"
name="firstName"
type="text"
onChange={formik.handleChange}
value={formik.values.firstName}
/>
{formik.errors.firstName ? <div>{formik.errors.firstName}</div> : null}
<label htmlFor="lastName">Last Name</label>
<input
id="lastName"
name="lastName"
type="text"
onChange={formik.handleChange}
value={formik.values.lastName}
/>
{formik.errors.lastName ? <div>{formik.errors.lastName}</div> : null}
<label htmlFor="email">Email Address</label>
<input
id="email"
name="email"
type="email"
onChange={formik.handleChange}
value={formik.values.email}
/>
{formik.errors.email ? <div>{formik.errors.email}</div> : null}
<button type="submit">Submit</button>
</form>
);
};
formik
returned from useFormik()
provides form’s states and helper methods such as:
handleSubmit
: a submission handlerhandleChange
: a change handler to pass to each<input>
,<select>
or<textarea>
values
: form’s current values
Notes
- We pass
id
andname
that matches the property we defined ininitialValues
Validation
Formik keeps track of form’s values
as well as its validation and error messages. In order to add validation with JavaScript, we can specify a custom validation function and pass it as validate
to the useFormik()
hook. If an error occurs, the custom validation function should produce an error object matching our values
/initialValues
.
formik.errors
is populated via the custom validation function. By default, Formik will validate after each keystroke, each input’s blur event, prior to submission.- The
onSubmit
function will be executed only if there are no errors
Visited fields
Most of the time, we only want to show a field’s error message after our user is done typing in that field.
Similar to errors
and values
, Formik keeps track of which fields have been visited, in an object called touched
.
- The keys of
touched
are field names, and the values oftouched
are booleanstrue
/false
. - We pass
formik.handleBlur
to each input’sonBlur
prop to take advantage oftouched
- Now that we’re tracking
touched
, we can change error message rendering logic to only show a given field’s error message if it exists and if the user has visited that field
- Now that we’re tracking
Reducing boilerplate
<Field>
: render an<input>
component that will grab the respectiveonChange
,onBlur
,value
props given aname
prop and pass them to the element as well as any props you pass to it.useField
: we can compose reusable input privmitive components that we can share around our application.const MyTextInput = ({ label, ...props }) => { // useField() returns [formik.getFieldProps(), formik.getFieldMeta()] // which we can spread on <input>. We can use field meta to show an error // message if the field is invalid and it has been touched (i.e. visited) const [field, meta] = useField(props); return ( <> <label htmlFor={props.id || props.name}>{label}</label> <input className="text-input" {...field} {...props} /> {meta.touched && meta.error ? ( <div className="error">{meta.error}</div> ) : null} </> ); };