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
idandnamethat 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.errorsis populated via the custom validation function. By default, Formik will validate after each keystroke, each input’s blur event, prior to submission.- The
onSubmitfunction 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
touchedare field names, and the values oftouchedare booleanstrue/false. - We pass
formik.handleBlurto each input’sonBlurprop 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,valueprops given anameprop 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} </> ); };