Handling forms with React Native using Formik and Yup

Bhavesh Daswani
Nov 3 · 9 min read
React Native Loves Formik

In this blog, I will explain to you how to handle form in react native with formik and yup library.

Formik

Formik is an awesome library that makes handling form in react or react native much much easier. Formik helps you in handling 3 most annoying parts in forms:

  • Getting values in and out of form state (Two way data binding)
  • Validation and error messages ( For this will be using Yup)
  • Handling form submission

Yup

Yup is a JavaScript object schema validator and object parser. Yup provide powerful validation in object-oriented way.

Lot About theory Let see Form validation in action:

It will work on your new or old react native project:

Step 1: Installing the package

Add Formik and Yup package to your project by running following command

For Formik:

npm install formik -S

or

yarn add formik

For Yup

npm install yup -S

or

yarn add yup

Step-2: Let's create the form

import React from "react";import { StyleSheet, Text, View, TextInput, Button } from "react-native";import { Formik } from "formik";import * as Yup from "yup";export default function App() {return (<View style={styles.container}><View style={styles.inputStyle}><TextInput placeholder="Email" /></View><Text style={{ color: "red" }}></Text><View style={styles.inputStyle}><TextInput placeholder="Password" /></View><Text style={{ color: "red" }}></Text><View style={styles.inputStyle}><TextInput placeholder="Confrim Password" /></View><Text style={{ color: "red" }}></Text><Button title="Submit" /></View>);}const styles = StyleSheet.create({container: {flex: 1,backgroundColor: "#fff",alignItems: "center",justifyContent: "center"},inputStyle: {padding: 10,width: "60%",borderWidth: 1,borderColor: "black"}});

The above is the simple form which contains email, password, confirm password, it will look like below

Simple form without formik

Step-3 : let's add Formik to the form

Let first add two-way data binding using formik

import React from "react";import { StyleSheet, Text, View, TextInput, Button } from "react-native";import { Formik } from "formik";import * as Yup from "yup";export default function App() {return (<Formik initialValues={{ email: "", password: "", confirmPassword: "" }}>{props => {return (<View style={styles.container}><View style={styles.inputStyle}><TextInputplaceholder="Email"value={props.values.email}onChangeText={props.handleChange("email")}/></View><Text style={{ color: "red" }}></Text><View style={styles.inputStyle}><TextInputplaceholder="Password"value={props.values.password}onChangeText={props.handleChange("password")}/></View><Text style={{ color: "red" }}></Text><View style={styles.inputStyle}><TextInput placeholder="Confrim Password"value={props.values.confirmPassword}onChangeText={props.handleChange("confirmPassword")}/></View><Text style={{ color: "red" }}></Text><Button title="Submit" /></View>);}}</Formik>);}const styles = StyleSheet.create({container: {flex: 1,backgroundColor: "#fff",alignItems: "center",justifyContent: "center",backgroundColor: "green"},inputStyle: {padding: 10,width: "60%",borderWidth: 1,borderColor: "black"}});

There are a lot of ways to add formik to our form, but I like wrapping our form inside <Formik></Fomik> component which makes it easier to understand.

Let understand the initialValues attribute and props we used to achieve two way databinding

<Formik initialValues={{ email: "", password: "", confirmPassword: "" }}>

initialValues set the initial values for email, password, confirm password fields but how these values are assigned to the respective input field, for this props comes into play let understand

<TextInputplaceholder="Email"value={props.values.email}onChangeText={props.handleChange("email")}/>

The values of the intialValues attributes are accessed by props.values and are updated by props.handleChange. So the value attribute of the TextInput component is bind to props.values.email, and to update email you have to bind to onChangeText event to props.handleChange method.

Step-4: Adding Validation using Yup

Let's Add Validation to our form using yup package

import React from "react";import {StyleSheet,Text,View,TextInput,Button,KeyboardAvoidingView} from "react-native";import { Formik } from "formik";import * as Yup from "yup";const FormValidationSchema = Yup.object().shape({email: Yup.string().email("Invalid Email.").required("Required"),password: Yup.string().min(6, "Too Short!").required("Required"),confirmPassword: Yup.string().required("Required").test("confirm-password-test","Password and confirm password should match",function(value) {return value === this.parent.password;})});export default function App() {return (<KeyboardAvoidingView style={{flex:1}} behavior='padding'><FormikinitialValues={{ email: "", password: "", confirmPassword: "" }}validationSchema={FormValidationSchema}>{props => {return (<View style={styles.container}><View style={styles.inputStyle}><TextInputplaceholder="Email"value={props.values.email}onChangeText={props.handleChange("email")}onBlur={props.handleBlur("email")}/></View><Text style={{ color: "red" }}>{props.touched.email && props.errors.email}</Text><View style={styles.inputStyle}><TextInputplaceholder="Password"value={props.values.password}onChangeText={props.handleChange("password")}onBlur={props.handleBlur("password")}/></View><Text style={{ color: "red" }}>{props.touched.password && props.errors.password}</Text><View style={styles.inputStyle}><TextInputplaceholder="Confrim Password"value={props.values.confirmPassword}onChangeText={props.handleChange("confirmPassword")}onBlur={props.handleBlur("confirmPassword")}/></View><Text style={{ color: "red" }}>{props.touched.confirmPassword && props.errors.confirmPassword}</Text><Button  title="Submit" /></View>);}}</Formik></KeyboardAvoidingView>);}const styles = StyleSheet.create({container: {flex: 1,backgroundColor: "#fff",alignItems: "center",justifyContent: "center",backgroundColor: "green"},inputStyle: {padding: 10,width: "60%",borderWidth: 1,borderColor: "black"}});

First, let's see the validation form output

Form with formik and validation

Let's understand the validation

const FormValidationSchema = Yup.object().shape({email: Yup.string().email("Invalid Email.").required("Email is Required"),password: Yup.string().min(6, "Too Short!").required("Password is Required"),confirmPassword: Yup.string().required("Confirm Password is Required").test("confirm-password-test","Password and confirm password should match",function(value) {return value === this.parent.password;})});

Here for email, I have put the validation as required and it should be of type email. One nice thing to notice is that you provide your validation message in validation rule as I have done .required(‘Email is Required’). For password, I have set a minimum 6 characters and required. For confirm password I have set that it should match with the password field and is required.

Two things you should notice. First is the key I used for validation like email, password,confirmPassword should match intialValues attribute value. Second, I have placed Yup validation schema (FormValidationSchema) outside the props component it is not necessary but if you are not using props for validation schema it is a good choice to place it out the component as it is not recreated every time component rerender.

<FormikinitialValues={{ email: "", password: "", confirmPassword: "" }}validationSchema={FormValidationSchema}>

To bind Yup validation schema with formik use validationSchema attribute and value would be yup validation schema object.

<TextInputplaceholder="Email"value={props.values.email}onChangeText={props.handleChange("email")}onBlur={props.handleBlur("email")}/>
<Text style={{ color: "red" }}>
{props.touched.email && props.errors.email}</Text>

In the input element, I have added onBlur element which triggers props.handeBlur method. This is useful to decide when to show the required validation message because by default form is empty so at that point, we do not want to show the required validation message, it should only be shown when the user selected the input element but does not fill it.

props.touched is an object which provide information about whether the user touched the input element or not. props.errors is an object which contains validation messages for the different fields we have in the form for example props.errors.email will contain validation message for the email field.

Step-5: Submitting the form and handing Server Side Error

After successful client-side validation using Yup and formik, we will submit the form detail to the server and handle any server-side error generated like duplicate email validation error etc. I will not go into sever side logic as this is out of scope for this article.

import React from "react";import {StyleSheet,Text,View,TextInput,Button,KeyboardAvoidingView,ActivityIndicator,Alert} from "react-native";import { Formik } from "formik";import * as Yup from "yup";const FormValidationSchema = Yup.object().shape({email: Yup.string().email("Invalid Email.").required("Required"),password: Yup.string().min(6, "Too Short!").required("Required"),confirmPassword: Yup.string().required("Required").test("confirm-password-test","Password and confirm password should match",function(value) {return value === this.parent.password;})});const signUp = ({ email,password }) =>new Promise((resolve, reject) => {setTimeout(() => {if (email === 'test@test.com') {reject(new Error("Duplicate email,please try again."));}resolve(true);}, 5000);});export default function App() {return (<KeyboardAvoidingView style={{flex:1}} behavior='padding'><FormikinitialValues={{ email: "", password: "", confirmPassword: "" }}validationSchema={FormValidationSchema}onSubmit={(values,actions)=>signUp({ email: values.email,password:values.password }).then(() => {Alert.alert('User Registered Succesfully.');}).catch(error => {actions.setFieldError('general', error.message);}).finally(() => {actions.setSubmitting(false);})}>{props => {return (<View style={styles.container}><View style={styles.inputStyle}><TextInputplaceholder="Email"value={props.values.email}onChangeText={props.handleChange("email")}onBlur={props.handleBlur("email")}/></View><Text style={{ color: "red" }}>{props.touched.email && props.errors.email}</Text><View style={styles.inputStyle}><TextInputplaceholder="Password"value={props.values.password}onChangeText={props.handleChange("password")}onBlur={props.handleBlur("password")}/></View><Text style={{ color: "red" }}>{props.touched.password && props.errors.password}</Text><View style={styles.inputStyle}><TextInputplaceholder="Confrim Password"value={props.values.confirmPassword}onChangeText={props.handleChange("confirmPassword")}onBlur={props.handleBlur("confirmPassword")}/></View><Text style={{ color: "red" }}>{props.touched.confirmPassword && props.errors.confirmPassword}</Text>{props.isSubmitting?<ActivityIndicator/>:<Button onPress={props.handleSubmit}  title="Submit" />}{<Text style={{ color: 'red' }}>{props.errors.general}</Text>}</View>);}}</Formik></KeyboardAvoidingView>);}const styles = StyleSheet.create({container: {flex: 1,backgroundColor: "#fff",alignItems: "center",justifyContent: "center",backgroundColor: "green"},inputStyle: {padding: 10,width: "60%",borderWidth: 1,borderColor: "black"}});

First, let's see the code in action and then we will understand it.

Submitting the form. When the form is being submitted the submit button is replaced by ActivityIndicator
Rendering Server Side Error
Success Response From Server

Let's Understand The final step that is form submission

{props.isSubmitting?<ActivityIndicator/>:<Button onPress={props.handleSubmit}  title="Submit" />}
{<Text style={{ color: 'red' }}>{props.errors.general}</Text>}

Form submission is handled by props.handleSubmit method. One interesting prop is props.isSubmitting which use full to handle whether the form is being submitted or not. Here i have use that prop to handle submit button. If isSubmitting is false then i will show the submit button else i will show a spinner which indicate the user that form submission is in progress.

props.errors.general is use to show server side error message. More on this is in below section.

const signUp = ({ email,password }) =>new Promise((resolve, reject) => {setTimeout(() => {if (email === 'test@test.com') {reject(new Error("Duplicate email,please try again."));}resolve(true);}, 5000);});

The above is the dummy function I created for mocking server process because our main goal is to understand form handling in react native so I don't want to make it complex by also including server-side code or logic.

<FormikinitialValues={{ email: "", password: "", confirmPassword: "" }}validationSchema={FormValidationSchema}onSubmit={(values,actions)=>signUp({ email: values.email,password:values.password }).then(() => {Alert.alert('User Registered Succesfully.');}).catch(error => {actions.setFieldError('general', error.message);}).finally(() => {actions.setSubmitting(false);})}>

In Fomik component I have added onSubmit method which is only called after all the client-side validation is successful. Here we make api request and handle server response. onSubmit method provide two argument values and actions. values argument contain all form fields values (value of email, password, confirmPassword) and actions argument is an object which helps to change the state of the form for example when the form submission is completed, we can change the state of the form using actions.setSubmitting(false) method.

Also, we can handle error response from the server by using actions.setFieldError(‘general’, error.message) method here ‘general’ key name is up to you. I use this key to show server-side error.


Note: I have not used secureTextEntry={true} for password and confirm password for making you understand clearly what i am typing in screenshot which is necessary for understanding of validation. I higly recommend to use secureTextEntry={true} for password and other sensitive text input

The Above article was an intro to form handling with react native using Formik and Yup. I just want to make clear the entire workflow of form submission like two-way data binding, validation and form submission, there is much more on this. Below Links help you in that matter.

Useful links:

Source Code for this demo can be found at github: https://github.com/bhaveshdaswani93/formik-with-react-native

Formik Github: https://github.com/jaredpalmer/formik

Formik Docs: https://jaredpalmer.com/formik/docs/overview

Formik Tutorial (That I, where i learned from): https://www.reactnativeschool.com/build-and-validate-forms-with-formik-and-yup/

Yup github: https://github.com/jquense/yup

Follow me on Github and Twitter

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade