@tgriesser/formik
v1.6.2
Published
Forms in React, without tears
Downloads
6
Maintainers
Readme
!! This is a fork !!
This is an opinionated fork of Formik.
- Removes
connect
andwithFormik
HOC's - Correctly types
<Field />
&<Formik />
, utilizing TS 2.9 generics, namespaces, etc. - Attempts to remove the render/children/component ambiguities & warnings in favor of TypeScript errors.
There are no guarentees this API will remain the same, semantically versioned, etc. So if you want to use it, fork it :)
Overview
Let's face it, forms are really verbose in React. To make matters worse, most form helpers do wayyyy too much magic and often have a significant performance cost associated with them. Formik is a small library that helps you with the 3 most annoying parts:
- Getting values in and out of form state
- Validation and error messages
- Handling form submission
By colocating all of the above in one place, Formik will keep things organized--making testing, refactoring, and reasoning about your forms a breeze.
Developer Experience
I (@jaredpalmer) wrote Formik while building a large internal administrative dashboard with @eonwhite. With around ~30 unique forms, it quickly became obvious that we could benefit by standardizing not just our input components but also the way in which data flowed through our forms.
Why not Redux-Form?
By now, you might be thinking, "Why didn't you just use Redux-Form?" Good question.
- According to our prophet Dan Abramov, form state is inherently ephemeral and local, so tracking it in Redux (or any kind of Flux library) is unnecessary
- Redux-Form calls your entire top-level Redux reducer multiple times ON EVERY SINGLE KEYSTROKE. This is fine for small apps, but as your Redux app grows, input latency will continue to increase if you use Redux-Form.
- Redux-Form is 22.5 kB minified gzipped (Formik is 7.8 kB)
My goal with Formik was to create a scalable, performant, form helper with a minimal API that does the really really annoying stuff, and leaves the rest up to you.
Testimonials
"I can't believe people ever put forms in redux, or did anything else other than this."
--James Long, Creator of Prettier and Actual Budget
"Formik. All day. All long."
--Ken Wheeler, Director of Open Source at Formidable Labs
"Been using @jaredpalmer's Formik lately at work – this is my all-time favorite way to handle forms"
--Brent Jackson, Creator of Rebass, styled-system, Compositor and many more
"Formik removes most of the moving parts involved in forms allowing me to move faster with more confidence."
--Kye Hohenberger, Creator of Emotion
Influences
Formik started by expanding on this little higher order component by Brent Jackson, some naming conventions from Redux-Form, and (most recently) the render props approach popularized by React-Motion and React-Router 4. Whether you have used any of the above or not, Formik only takes a few minutes to get started with.
Installation
Add Formik to your project.
npm i formik --save
You can also try before you buy with this demo of Formik on CodeSandbox.io or on Expo Snack
Demos
- Basics
- Sync Validation
- Building your own input primitives
- Working with 3rd-party inputs #1: react-select
- Working with 3rd-party inputs #2: Draft.js
- Accessing React lifecycle functions
- React Native
Talks
- An Introduction to Formik by Jared Palmer @ Spotify NYC. August 15th, 2017.
Community Articles / Tutorials
- Better React Forms with Formik
- The Joy of Forms with React and Formik
- Painless React Forms with Formik
The gist
Formik keeps track of your form's state and then exposes it plus a few reusable
methods and event handlers (handleChange
, handleBlur
, and handleSubmit
) to
your form via props
. handleChange
and handleBlur
work exactly as
expected--they use a name
or id
attribute to figure out which field to
update.
// Render Prop
import React from 'react';
import { Formik } from 'formik';
const Basic = () => (
<div>
<h1>My Form</h1>
<p>This can be anywhere in your application</p>
{/*
The benefit of the render prop approach is that you have full access to React's
state, props, and composition model. Thus there is no need to map outer props
to values...you can just set the initial values, and if they depend on props / state
then--boom--you can directly access to props / state.
The render prop accepts your inner form component, which you can define separately or inline
totally up to you:
- `<Formik render={props => <form>...</form>}>`
- `<Formik component={InnerForm}>`
- `<Formik>{props => <form>...</form>}</Formik>` (identical to as render, just written differently)
*/}
<Formik
initialValues={{
email: '',
password: '',
}}
validate={values => {
// same as above, but feel free to move this into a class method now.
let errors = {};
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;
}}
onSubmit={(
values,
{ setSubmitting, setErrors /* setValues and other goodies */ }
) => {
LoginToMyApp(values).then(
user => {
setSubmitting(false);
// do whatevs...
// props.updateUser(user)
},
errors => {
setSubmitting(false);
// Maybe transform your API's errors into the same shape as Formik's
setErrors(transformMyApiErrors(errors));
}
);
}}
render={({
values,
errors,
touched,
handleChange,
handleBlur,
handleSubmit,
isSubmitting,
}) => (
<form onSubmit={handleSubmit}>
<input
type="email"
name="email"
onChange={handleChange}
onBlur={handleBlur}
value={values.email}
/>
{touched.email && errors.email && <div>{errors.email}</div>}
<input
type="password"
name="password"
onChange={handleChange}
onBlur={handleBlur}
value={values.password}
/>
{touched.password && errors.password && <div>{errors.password}</div>}
<button type="submit" disabled={isSubmitting}>
Submit
</button>
</form>
)}
/>
</div>
);
export default Basic;
Complementary Packages
As you can see above, validation is left up to you. Feel free to write your own
validators or use a 3rd party library. Personally, I use
Yup for object schema validation. It has an
API that's pretty similar Joi /
React PropTypes but is small enough
for the browser and fast enough for runtime usage. Because I :heart: Yup sooo
much, Formik has a special config option / prop for Yup called
validationSchema
which will automatically transform Yup's validation errors
into a pretty object whose keys match values
and touched
. Anyways, you
can install Yup from npm...
npm install yup --save
Table of Contents
- Guides
- API
<Formik />
- Formik render methods
- Formik props
dirty: boolean
errors: { [field: string]: string }
handleBlur: (e: any) => void
handleChange: (e: React.ChangeEvent<any>) => void
handleReset: () => void
handleSubmit: (e: React.FormEvent<HTMLFormEvent>) => void
isSubmitting: boolean
isValid: boolean
isValidating: boolean
resetForm: (nextValues?: Values) => void
setErrors: (fields: { [field: string]: string }) => void
setFieldError: (field: string, errorMsg: string | undefined) => void
setFieldTouched: (field: string, isTouched: boolean, shouldValidate?: boolean) => void
submitForm: () => void
submitCount: number
setFieldValue: (field: string, value: any, shouldValidate?: boolean) => void
setStatus: (status?: any) => void
setSubmitting: (isSubmitting: boolean) => void
setTouched: (fields: { [field: string]: boolean }) => void
setValues: (fields: { [field: string]: any }) => void
status?: any
touched: { [field: string]: boolean }
values: { [field: string]: any }
validateForm: (values?: any) => void
validateField: (field: string) => void
component
render: (props: FormikProps<Values>) => ReactNode
enableReinitialize?: boolean
isInitialValid?: boolean
initialValues?: Values
onReset?: (values: Values, formikBag: FormikBag) => void
onSubmit: (values: Values, formikBag: FormikBag) => void
validate?: (values: Values) => FormikErrors<Values> | Promise<any>
validateOnBlur?: boolean
validateOnChange?: boolean
validationSchema?: Schema | (() => Schema)
<Field />
<FieldArray />
<Form />
- Organizations and projects using Formik
- Authors
- Contributors
Guides
Basics
Imagine you want to build a form that lets you edit user data. However, your user API has nested objects like so.
{
id: string,
email: string,
social: {
facebook: string,
twitter: string,
// ...
}
}
When we are done we want our dialog to accept just a user
, updateUser
, and
onClose
props.
// User.js
import React from 'react';
import Dialog from 'MySuperDialog';
import { Formik } from 'formik';
const EditUserDialog = ({ user, updateUser, onClose }) => {
return (
<Dialog onClose={onClose}>
<h1>Edit User</h1>
<Formik
initialValues={user /** { email, social } */}
onSubmit={(values, actions) => {
CallMyApi(user.id, values).then(
updatedUser => {
actions.setSubmitting(false);
updateUser(updatedUser), onClose();
},
error => {
actions.setSubmitting(false);
actions.setErrors(transformMyAPIErrorToAnObject(error));
}
);
}}
render={({
values,
errors,
touched,
handleBlur,
handleChange,
handleSubmit,
isSubmitting,
}) => (
<form onSubmit={handleSubmit}>
<input
type="email"
name="email"
onChange={handleChange}
onBlur={handleBlur}
value={values.email}
/>
{errors.email && touched.email && <div>{errors.email}</div>}
<input
type="text"
name="social.facebook"
onChange={handleChange}
onBlur={handleBlur}
value={values.social.facebook}
/>
{errors.social &&
errors.social.facebook &&
touched.facebook && <div>{errors.social.facebook}</div>}
<input
type="text"
name="social.twitter"
onChange={handleChange}
onBlur={handleBlur}
value={values.social.twitter}
/>
{errors.social &&
errors.social.twitter &&
touched.twitter && <div>{errors.social.twitter}</div>}
<button type="submit" disabled={isSubmitting}>
Submit
</button>
</form>
)}
/>
</Dialog>
);
};
To make writing forms less verbose. Formik comes with a few helpers to save you key strokes.
<Field>
<Form />
This is the exact same form as before, but written with <Form />
and
<Field />
:
// EditUserDialog.js
import React from 'react';
import Dialog from 'MySuperDialog';
import { Formik, Field, Form } from 'formik';
const EditUserDialog = ({ user, updateUser, onClose }) => {
return (
<Dialog onClose={onClose}>
<h1>Edit User</h1>
<Formik
initialValues={user /** { email, social } */}
onSubmit={(values, actions) => {
CallMyApi(user.id, values).then(
updatedUser => {
actions.setSubmitting(false);
updateUser(updatedUser), onClose();
},
error => {
actions.setSubmitting(false);
actions.setErrors(transformMyAPIErrorToAnObject(error));
}
);
}}
render={({ errors, touched, isSubmitting }) => (
<Form>
<Field type="email" name="email" />
{errors.email && touched.email && <div>{errors.email}</div>}
<Field type="text" name="social.facebook" />
{errors.social.facebook &&
touched.social.facebook && <div>{errors.social.facebook}</div>}
<Field type="text" name="social.twitter" />
{errors.social.twitter &&
touched.social.twitter && <div>{errors.social.twitter}</div>}
<button type="submit" disabled={isSubmitting}>
Submit
</button>
</Form>
)}
/>
</Dialog>
);
};
React Native
Formik is 100% compatible with React Native and React Native Web. However, because of differences between ReactDOM's and React Native's handling of forms and text input, there are two differences to be aware of. This section will walk you through them and what I consider to be best practices.
Before going any further, here's a super minimal gist of how to use Formik with React Native that demonstrates the key differences:
// Formik x React Native example
import React from 'react';
import { Button, TextInput, View } from 'react-native';
import { withFormik } from 'formik';
const enhancer = withFormik({
/*...*/
});
const MyReactNativeForm = props => (
<View>
<TextInput
onChangeText={props.handleChange('email')}
onBlur={props.handleBlur('email')}
value={props.values.email}
/>
<Button onPress={props.handleSubmit} title="Submit" />
</View>
);
export default enhancer(MyReactNativeForm);
As you can see above, the notable differences between using Formik with React DOM and React Native are:
- Formik's
props.handleSubmit
is passed to a<Button onPress={...} />
instead of HTML<form onSubmit={...} />
component (since there is no<form />
element in React Native). <TextInput />
uses Formik'sprops.handleChange(fieldName)
andhandleBlur(fieldName)
instead of directly assigning the callbacks to props, because we have to get thefieldName
from somewhere and with ReactNative we can't get it automatically like for web (using input name attribute). You can also usesetFieldValue(fieldName, value)
andsetTouched(fieldName, bool)
as an alternative.
Avoiding new functions in render
If for any reason you wish to avoid creating new functions on each render, I
suggest treating React Native's <TextInput />
as if it were another 3rd party
custom input element:
- Write your own class wrapper around the custom input element
- Pass the custom component
props.setFieldValue
instead ofprops.handleChange
- Use a custom change handler callback that calls whatever you passed-in
setFieldValue
as (in this case we'll match the React Native TextInput API and call itthis.props.onChangeText
for parity).
// FormikReactNativeTextInput.js
import * as React from 'react';
import { TextInput } from 'react-native';
export default class FormikReactNativeTextInput extends React.Component {
handleChange = (value: string) => {
// remember that onChangeText will be Formik's setFieldValue
this.props.onChangeText(this.props.name, value);
};
render() {
// we want to pass through all the props except for onChangeText
const { onChangeText, ...otherProps } = this.props;
return (
<TextInput
onChangeText={this.handleChange}
{...otherProps} // IRL, you should be more explicit when using TS
/>
);
}
}
Then you could just use this custom input as follows:
// MyReactNativeForm.js
import { View, Button } from 'react-native';
import TextInput from './FormikReactNativeTextInput';
import { Formik } from 'formik';
const MyReactNativeForm = props => (
<View>
<Formik
onSubmit={(values, actions) => {
setTimeout(() => {
console.log(JSON.stringify(values, null, 2));
actions.setSubmitting(false);
}, 1000);
}}
render={props => (
<View>
<TextInput
name="email"
onChangeText={props.setFieldValue}
value={props.values.email}
/>
<Button title="submit" onPress={props.handleSubmit} />
</View>
)}
/>
</View>
);
export default MyReactNativeForm;
Using Formik with TypeScript
The Formik source code is written in TypeScript, so you can rest assured that
types will always be up to date. As a mental model, Formik's types are very
similar to React Router 4's <Route>
.
Render props (<Formik />
and <Field />
)
import * as React from 'react';
import { Formik, FormikProps, Form, Field, FieldProps } from 'formik';
interface MyFormValues {
firstName: string;
}
export const MyApp: React.SFC<{} /* whatever */> = () => {
return (
<div>
<h1>My Example</h1>
<Formik
initialValues={{ firstName: '' }}
onSubmit={(values: MyFormValues) => alert(JSON.stringify(values))}
render={(formikBag: FormikProps<MyFormValues>) => (
<Form>
<Field
name="firstName"
render={({ field, form }: FieldProps<MyFormValues>) => (
<div>
<input type="text" {...field} placeholder="First Name" />
{form.touched.firstName &&
form.errors.firstName &&
form.errors.firstName}
</div>
)}
/>
</Form>
)}
/>
</div>
);
};
How Form Submission Works
To submit a form in Formik, you need to somehow fire off the provided handleSubmit(e)
or submitForm
prop. When you call either of these methods, Formik will execute the following (pseudo code) each time:
- "Pre-submit"
- Touch all fields
- Set
isSubmitting
totrue
- Increment
submitCount
+ 1
- "Validation"
- Set
isValidating
totrue
- Run all field-level validations,
validate
, andvalidationSchema
asynchronously and deeply merge results - Are there any errors?
- Yes: Abort submission. Set
isValidating
tofalse
, seterrors
, setisSubmitting
tofalse
- No: Set
isValidating
tofalse
, proceed to "Submission"
- Yes: Abort submission. Set
- Set
- "Submission"
- Proceed with running your submission handler (i.e.
onSubmit
orhandleSubmit
) - you call
setSubmitting(false)
in your handler to finish the cycle
- Proceed with running your submission handler (i.e.
Frequently Asked Questions
If isValidating
is false
and isSubmitting
is true
.
It is common practice to only show an input's errors in the UI if it has been visited (a.k.a "touched"). Before submitting a form, Formik touches all fields so that all errors that may have been hidden will now be visible.
Disable whatever is triggering submission if isSubmitting
is true
.
If isValidating
is true
and isSubmitting
is true
.
API
<Formik />
<Formik>
is a component that helps you with building forms. It uses a render
props pattern made popular by libraries like React Motion and React Router.
import React from 'react';
import { Formik } from 'formik';
const BasicExample = () => (
<div>
<h1>My Form</h1>
<Formik
initialValues={{ name: 'jared' }}
onSubmit={(values, actions) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
actions.setSubmitting(false);
}, 1000);
}}
render={props => (
<form onSubmit={props.handleSubmit}>
<input
type="text"
onChange={props.handleChange}
onBlur={props.handleBlur}
value={props.values.name}
name="name"
/>
{props.errors.name && <div id="feedback">{props.errors.name}</div>}
<button type="submit">Submit</button>
</form>
)}
/>
</div>
);
Formik render methods
There are three ways to render things with <Formik />
<Formik component>
<Formik render>
<Formik children>
Formik props
All three render methods will be passed the same props:
dirty: boolean
Returns true
if values are not deeply equal from initial values, false
otherwise.
dirty
is a readonly computed property and should not be mutated directly.
errors: { [field: string]: string }
Form validation errors. Should match the shape of your form's values
defined
in initialValues
. If you are using validationSchema
(which you should be),
keys and shape will match your schema exactly. Internally, Formik transforms raw
Yup validation errors
on your behalf. If you are using validate
, then that function will determine
the errors
objects shape.
handleBlur: (e: any) => void
onBlur
event handler. Useful for when you need to track whether an input has
been touched
or not. This should be passed to <input onBlur={handleBlur} ... />
DOM-only. Use setFieldTouched
in React Native.
handleChange: (e: React.ChangeEvent<any>) => void
General input change event handler. This will update the values[key]
where
key
is the event-emitting input's name
attribute. If the name
attribute is
not present, handleChange
will look for an input's id
attribute. Note:
"input" here means all HTML inputs.
DOM-only. Use setFieldValue
in React Native.
handleReset: () => void
Reset handler. Will reset the form to its initial state. This should be passed
to <button onClick={handleReset}>...</button>
handleSubmit: (e: React.FormEvent<HTMLFormEvent>) => void
Submit handler. This should be passed to <form onSubmit={props.handleSubmit}>...</form>
. To learn more about the submission process, see How Form Submission Works.
isSubmitting: boolean
Submitting state of the form. Returns true
if submission is in progress and false
otherwise. IMPORTANT: Formik will set this to true
as soon as submission is attempted. To learn more about the submission process, see How Form Submission Works.
isValid: boolean
Returns true
if the there are no errors
, or the result of
isInitialValid
the form if is in "pristine" condition (i.e. not dirty
)).
isValidating: boolean
Returns true
if Formik is running any validation function, false
otherwise. To learn more about what happens with isValidating
during the submission process, see How Form Submission Works.
resetForm: (nextValues?: Values) => void
Imperatively reset the form. This will clear errors
and touched
, set
isSubmitting
to false
, isValidating
to false
, and rerun mapPropsToValues
with the current
WrappedComponent
's props
or what's passed as an argument. The latter is
useful for calling resetForm
within componentWillReceiveProps
.
setErrors: (fields: { [field: string]: string }) => void
Set errors
imperatively.
setFieldError: (field: string, errorMsg: string) => void
Set the error message of a field imperatively. field
should match the key of
errors
you wish to update. Useful for creating custom input error handlers.
setFieldTouched: (field: string, isTouched: boolean, shouldValidate?: boolean) => void
Set the touched state of a field imperatively. field
should match the key of
touched
you wish to update. Useful for creating custom input blur handlers. Calling this method will trigger validation to run if validateOnBlur
is set to true
(which it is by default). You can also explicitly prevent/skip validation by passing a third argument as false
.
submitForm: () => void
Trigger a form submission.
submitCount: number
Number of times user tried to submit the form. Increases when handleSubmit
is called, resets after calling
handleReset
. submitCount
is readonly computed property and should not be mutated directly.
setFieldValue: (field: string, value: any, shouldValidate?: boolean) => void
Set the value of a field imperatively. field
should match the key of
values
you wish to update. Useful for creating custom input change handlers. Calling this will trigger validation to run if validateOnChange
is set to true
(which it is by default). You can also explicitly prevent/skip validation by passing a third argument as false
.
setStatus: (status?: any) => void
Set a top-level status
to anything you want imperatively. Useful for
controlling arbitrary top-level state related to your form. For example, you can
use it to pass API responses back into your component in handleSubmit
.
setSubmitting: (isSubmitting: boolean) => void
Set isSubmitting
imperatively.
setTouched: (fields: { [field: string]: boolean }) => void
Set touched
imperatively.
setValues: (fields: { [field: string]: any }) => void
Set values
imperatively.
status?: any
A top-level status object that you can use to represent form state that can't otherwise be expressed/stored with other methods. This is useful for capturing and passing through API responses to your inner component.
status
should only be modifed by calling
setStatus: (status?: any) => void
touched: { [field: string]: boolean }
Touched fields. Each key corresponds to a field that has been touched/visited.
values: { [field: string]: any }
Your form's values. Will have the shape of the result of mapPropsToValues
(if specified) or all props that are not functions passed to your wrapped
component.
validateForm: (values?: any) => void
Imperatively call your validate
or [validateSchema
] depending on what was specified. You can optionally pass values to validate against and this modify Formik state accordingly, otherwise this will use the current values
of the form.
validateField: (field: string) => void
Imperatively call field's validate
function if specified for given field. Formik will use the current field value.
component
<Formik component={ContactForm} />;
const ContactForm = ({
handleSubmit,
handleChange,
handleBlur,
values,
errors,
}) => (
<form onSubmit={handleSubmit}>
<input
type="text"
onChange={handleChange}
onBlur={handleBlur}
value={values.name}
name="name"
/>
{errors.name && <div>{errors.name}</div>}
<button type="submit">Submit</button>
</form>
};
render: (props: FormikProps<Values>) => ReactNode
<Formik render={props => <ContactForm {...props} />} />
<Formik
render={({ handleSubmit, handleChange, handleBlur, values, errors }) => (
<form onSubmit={handleSubmit}>
<input
type="text"
onChange={handleChange}
onBlur={handleBlur}
value={values.name}
name="name"
/>
{errors.name &&
<div>
{errors.name}
</div>}
<button type="submit">Submit</button>
</form>
)}
/>
children: func
<Formik children={props => <ContactForm {...props} />} />
// or...
<Formik>
{({ handleSubmit, handleChange, handleBlur, values, errors }) => (
<form onSubmit={handleSubmit}>
<input
type="text"
onChange={handleChange}
onBlur={handleBlur}
value={values.name}
name="name"
/>
{errors.name &&
<div>
{errors.name}
</div>}
<button type="submit">Submit</button>
</form>
)}
</Formik>
enableReinitialize?: boolean
Default is false
. Control whether Formik should reset the form if
[initialValues
] changes (using deep equality).
isInitialValid?: boolean
Default is false
. Control the initial value of isValid
prop prior to
mount. You can also pass a function. Useful for situations when you want to
enable/disable a submit and reset buttons on initial mount.
initialValues?: Values
Initial field values of the form, Formik will make these values available to
render methods component as props.values
.
Even if your form is empty by default, you must initialize all fields with initial values otherwise React will throw an error saying that you have changed an input from uncontrolled to controlled.
Note: initialValues
not available to the higher-order component, use
mapPropsToValues
instead.
onReset?: (values: Values, formikBag: FormikBag) => void
Your optional form reset handler. It is passed your forms values
and the
"FormikBag".
onSubmit: (values: Values, formikBag: FormikBag) => void
Your form submission handler. It is passed your forms values
and the
"FormikBag", which includes an object containing a subset of the
injected props and methods (i.e. all the methods
with names that start with set<Thing>
+ resetForm
) and any props that were
passed to the the wrapped component.
Note: errors
, touched
, status
and all event handlers are NOT
included in the FormikBag
.
validate?: (values: Values) => FormikErrors<Values> | Promise<any>
Note: I suggest using validationSchema
and Yup for validation. However,
validate
is a dependency-free, straightforward way to validate your forms.
Validate the form's values
with function. This function can either be:
- Synchronous and return an
errors
object.
// Synchronous validation
const validate = (values, props) => {
let errors = {};
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;
};
- Asynchronous and return a Promise that's error in an
errors
object
// Async Validation
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
const validate = (values, props) => {
return sleep(2000).then(() => {
let errors = {};
if (['admin', 'null', 'god'].includes(values.username)) {
errors.username = 'Nice try';
}
// ...
if (Object.keys(errors).length) {
throw errors;
}
});
};
validateOnBlur?: boolean
Default is true
. Use this option to run validations on blur
events. More
specifically, when either handleBlur
, setFieldTouched
, or setTouched
are called.
validateOnChange?: boolean
Default is true
. Use this option to tell Formik to run validations on change
events and change
-related methods. More specifically, when either
handleChange
, setFieldValue
, or setValues
are called.
validationSchema?: Schema | (() => Schema)
A Yup schema or a function that returns a Yup
schema. This is used for validation. Errors are mapped by key to the inner
component's errors
. Its keys should match those of values
.
<Field />
<Field />
will automagically hook up inputs to Formik. It uses the name
attribute to match up with Formik state. <Field />
will default to an
<input />
element. To change the underlying element of <Field />
, specify a
component
prop. It can either be a string like select
or another React
component. <Field />
can also take a render
prop.
import React from 'react';
import { Formik, Field } from 'formik';
const Example = () => (
<div>
<h1>My Form</h1>
<Formik
initialValues={{ email: '', color: 'red', firstName: '' }}
onSubmit={(values, actions) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
actions.setSubmitting(false);
}, 1000);
}}
render={(props: FormikProps<Values>) => (
<form onSubmit={props.handleSubmit}>
<Field type="email" name="email" placeholder="Email" />
<Field component="select" name="color">
<option value="red">Red</option>
<option value="green">Green</option>
<option value="blue">Blue</option>
</Field>
<Field name="firstName" component={CustomInputComponent} />
<Field
name="lastName"
render={({ field /* _form */ }) => (
<input {...field} placeholder="firstName" />
)}
/>
<button type="submit">Submit</button>
</form>
)}
/>
</div>
);
const CustomInputComponent: React.SFC<
FieldProps<Values> & CustomInputProps
> = ({
field, // { name, value, onChange, onBlur }
form: { touched, errors }, // also values, setXXXX, handleXXXX, dirty, isValid, status, etc.
...props
}) => (
<div>
<input type="text" {...field} {...props} />
{touched[field.name] &&
errors[field.name] && <div className="error">{errors[field.name]}</div>}
</div>
);
validate?: (value: any) => undefined | string | Promise<any>
You can run independent field-level validations by passing a function to the
validate
prop. The function will respect the validateOnBlur
and
validateOnChange
config/props specified in the <Field>'s
parent <Formik>
. This function can be either be:
- Synchronous and if invalid, return a
string
containing the error message or returnundefined
.
// Synchronous validation for Field
const validate = value => {
let errorMessage;
if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(value)) {
errorMessage = 'Invalid email address';
}
return errorMessage;
};
async: Return a Promise that throws a
string
containing the error message. This works like Formik'svalidate
, but instead of returning anerrors
object, it's just astring
.Asynchronous and return a Promise that's error is an string with the error message
// Async validation for Field
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
const validate = value => {
return sleep(2000).then(() => {
if (['admin', 'null', 'god'].includes(value)) {
throw 'Nice try';
}
});
};
Note: To allow for i18n libraries, the TypeScript typings for validate
are
slightly relaxed and allow you to return a Function
(e.g. i18n('invalid')
).
Refs
When you are not using a custom component and you need to access the underlying DOM node created by Field
(e.g. to call focus
), pass the callback to the innerRef
prop instead.
<FieldArray />
<FieldArray />
is a component that helps with common array/list manipulations. You pass it a name
property with the path to the key within values
that holds the relevant array. <FieldArray />
will then give you access to array helper methods via render props. For convenience, calling these methods will trigger validation and also manage touched
for you.
import React from 'react';
import { Formik, Form, Field, FieldArray } from 'formik';
// Here is an example of a form with an editable list.
// Next to each input are buttons for insert and remove.
// If the list is empty, there is a button to add an item.
export const FriendList = () => (
<div>
<h1>Friend List</h1>
<Formik
initialValues={{ friends: ['jared', 'ian', 'brent'] }}
onSubmit={values =>
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
}, 500)
}
render={({ values }) => (
<Form>
<FieldArray
name="friends"
render={arrayHelpers => (
<div>
{values.friends && values.friends.length > 0 ? (
values.friends.map((friend, index) => (
<div key={index}>
<Field name={`friends.${index}`} />
<button
type="button"
onClick={() => arrayHelpers.remove(index)} // remove a friend from the list
>
-
</button>
<button
type="button"
onClick={() => arrayHelpers.insert(index, '')} // insert an empty string at a position
>
+
</button>
</div>
))
) : (
<button type="button" onClick={() => arrayHelpers.push('')}>
{/* show this when user has removed all friends from the list */}
Add a friend
</button>
)}
<div>
<button type="submit">Submit</button>
</div>
</div>
)}
/>
</Form>
)}
/>
</div>
);
name: string
The name or path to the relevant key in values
.
validateOnChange?: boolean
Default is true
. Determines if form validation should or should not be run after any array manipulations.
FieldArray Array of Objects
You can also iterate through an array of objects, by following a convention of object[index]property
or object.index.property
for the name attributes of <Field />
or <input />
elements in <FieldArray />
.
<Form>
<FieldArray
name="friends"
render={arrayHelpers => (
<div>
{values.friends.map((friend, index) => (
<div key={index}>
<Field name={`friends[${index}]name`} />
<Field name={`friends.${index}.age`} /> // both these conventions do
the same
<button type="button" onClick={() => arrayHelpers.remove(index)}>
-
</button>
</div>
))}
<button
type="button"
onClick={() => arrayHelpers.push({ name: '', age: '' })}
>
+
</button>
</div>
)}
/>
</Form>
FieldArray Validation Gotchas
Validation can be tricky with <FieldArray>
.
If you use validationSchema
and your form has array validation requirements (like a min length) as well as nested array field requirements, displaying errors can be tricky. Formik/Yup will show validation errors inside out. For example,
const schema = Yup.object().shape({
friends: Yup.array()
.of(
Yup.object().shape({
name: Yup.string()
.min(4, 'too short')
.required('Required'), // these constraints take precedence
salary: Yup.string()
.min(3, 'cmon')
.required('Required'), // these constraints take precedence
})
)
.required('Must have friends') // these constraints are shown if and only if inner constraints are satisfied
.min(3, 'Minimum of 3 friends'),
});
Since Yup and your custom validation function should always output error messages as strings, you'll need to sniff whether your nested error is an array or a string when you go to display it.
So...to display 'Must have friends'
and 'Minimum of 3 friends'
(our example's array validation contstraints)...
Bad
// within a `FieldArray`'s render
const FriendArrayErrors = errors =>
errors.friends ? <div>{errors.friends}</div> : null; // app will crash
Good
// within a `FieldArray`'s render
const FriendArrayErrors = errors =>
typeof errors.friends === 'string' ? <div>{errors.friends}</div> : null;
For the nested field errors, you should assume that no part of the object is defined unless you've checked for it. Thus, you may want to do yourself a favor and make a custom <ErrorMessage />
component that looks like this:
import { Field, getIn } from 'formik';
const ErrorMessage = ({ name }) => (
<Field
name={name}
render={({ form }) => {
const error = getIn(form.errors, name);
const touch = getIn(form.touched, name);
return touch && error ? error : null;
}}
/>
);
// Usage
<ErrorMessage name="friends[0].name" />; // => null, 'too short', or 'required'
NOTE: In Formik v0.12 / 1.0, a new meta
prop may be added to Field
and FieldArray
that will give you relevant metadata such as error
& touch
, which will save you from having to use Formik or lodash's getIn or checking if the path is defined on your own.
FieldArray Helpers
The following methods are made available via render props.
push: (obj: any) => void
: Add a value to the end of an arrayswap: (indexA: number, indexB: number) => void
: Swap two values in an arraymove: (from: number, to: number) => void
: Move an element in an array to another indexinsert: (index: number, value: any) => void
: Insert an element at a given index into the arrayunshift: (value: any) => number
: Add an element to the beginning of an array and return its lengthremove<T>(index: number): T | undefined
: Remove an element at an index of an array and return itpop<T>(): T | undefined
: Remove and return value from the end of the array
FieldArray render methods
There are two ways to render things with <FieldArray />
<FieldArray name="..." component>
<FieldArray name="..." render>
render: (arrayHelpers: ArrayHelpers) => React.ReactNode
import React from 'react';
import { Formik, Form, Field, FieldArray } from 'formik'
export const FriendList = () => (
<div>
<h1>Friend List</h1>
<Formik
initialValues={{ friends: ['jared', 'ian', 'brent'] }}
onSubmit={...}
render={formikProps => (
<FieldArray
name="friends"
render={({ move, swap, push, insert, unshift, pop }) => (
<Form>
{/*... use these however you want */}
</Form>
)}
/>
/>
</div>
);
component: React.ReactNode
import React from 'react';
import { Formik, Form, Field, FieldArray } from 'formik'
export const FriendList = () => (
<div>
<h1>Friend List</h1>
<Formik
initialValues={{ friends: ['jared', 'ian', 'brent'] }}
onSubmit={...}
render={formikProps => (
<FieldArray
name="friends"
component={MyDynamicForm}
/>
/>
</div>
);
// In addition to the array helpers, Formik state and helpers
// (values, touched, setXXX, etc) are provided through a `form`
// prop
export const MyDynamicForm = ({
move, swap, push, insert, unshift, pop, form
}) => (
<Form>
{/** whatever you need to do */}
</Form>
);
<Form />
Like <Field />
, <Form />
is a helper component you can use to save time. It is
tiny wrapper around <form onSubmit={context.formik.handleSubmit} />
. This
means you don't need to explictly type out <form onSubmit={props.handleSubmit} />
if you don't want to.
ReactDOM only
import React from 'react';
import { Formik, Field, Form } from 'formik';
const Example = () => (
<div>
<h1>My Form</h1>
<Formik
initialValues={{ email: '', color: 'red' }}
onSubmit={(values, actions) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
actions.setSubmitting(false);
}, 1000);
}}
component={MyForm}
/>
</div>
);
const MyForm = () => (
<Form>
<Field type="email" name="email" placeholder="Email" />
<Field component="select" name="color">
<option value="red">Red</option>
<option value="green">Green</option>
<option value="blue">Blue</option>
</Field>
<button type="submit">Submit</button>
</Form>
);
Organizations and projects using Formik
List of organizations and projects using Formik
Authors
- Jared Palmer @jaredpalmer
- Ian White @eonwhite
Contributors
Formik is made with <3 thanks to these wonderful people (emoji key):
| Jared Palmer💬 💻 🎨 📖 💡 🤔 👀 ⚠️ | Ian White💬 🐛 💻 📖 🤔 👀 | Andrej Badin💬 🐛 📖 | Adam Howard💬 🐛 🤔 👀 | Vlad Shcherbin💬 🐛 🤔 | Brikou CARRE🐛 📖 | Sam Kvale🐛 💻 ⚠️ | | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | | Jon Tansey🐛 💻 | Tyler Martinez🐛 📖 |
This project follows the all-contributors specification. Contributions of any kind welcome!
MIT License.