React Native Form Validation with Formik and Yup

React Native Form Validation

The form is a very basic need for any website or application. And form validation is required to collect the right information. In this article, we are going to learn Form validation with Formik and Yup for React Native application.

Let’s start by creating a new project with expo CLI, we are going to name it form-validation. You can use any name for your project.

expo init form-validation

The above command will create and set up everything that we require to run our application. Now move to your project directory and check that everything is fine by running your project.

expo start

Install Formik and Yup

The next step is to install the Formik and Yup for the Form Validation. We are going to use the React Native Elements package to quickly set up the UI for our Form. Let’s install the dependencies through the npm command.

npm install react-native-elements formik yup

Create Reusable Components

It’s best practice to create the component for the reusable elements. In our case, we need the heading, input field, and the button component. Let’s create reusable components for the followings.

Let’s first create a components directory to your project directory and create the following files i.e. Heading.js, InputField.js and InputButton.js

Heading Component

Open the Heading.js file and add the following code.

import React from 'react'
import { Text } from 'react-native-elements'
import { StyleSheet } from 'react-native'

const Heading = ({ title, ...size }) => (
    <Text {...size} style={styles.heading}>{ title }</Text>
)

const styles = StyleSheet.create({
    heading: {
        color: '#533263', 
        fontWeight: 'bold'
    }
});

export default Heading

The above codes work similarly to the “HMTL headings” that support the H1-H6 and a title property to display the text.

InputField Component

Paste the following codes to the InputField.js file.

import React from 'react'
import { Input } from 'react-native-elements'
import { StyleSheet, View } from 'react-native'
import { Ionicons } from '@expo/vector-icons'

const InputField = ({ iconName, iconColor, name, placeholder, label, value, ...rest }) => (
    <View style={styles.container}>
        <Input
            {...rest}
            leftIcon={<Ionicons name={iconName} size={28} color={iconColor} />}
            leftIconContainerStyle={styles.iconStyle}
            placeholderTextColor='grey'
            name={name}
            value={value}
            label={label}
            placeholder={placeholder}
            style={styles.input}
        />
    </View>
)

const styles = StyleSheet.create({
    container: {
        marginBottom: 15,
        width: '100%'
    },
    iconStyle: {
        marginRight: 10
    }
})
  
export default InputField;

Basically, we reconstructed the react-native-elements Input component as per our application UI. We supported the following properties for component i.e. iconName, iconColor, name, placeholder, label, value. The ...rest property for all the properties that we have not defined but can be supported by our component.

InputButton Component

Now open the InputButton.js file and paste the following code.

import React from 'react'
import { Button } from 'react-native-elements'

const InputButton = ({ title, buttonType, buttonColor, ...rest }) => (
    <Button
        {...rest}
        type={buttonType}
        title={title}
        buttonStyle={{ borderColor: buttonColor, borderRadius: 3 }}
        titleStyle={{ color: '#ffffff' }}
    />
)

export default InputButton

It’s similar to the InputField component, but this time we have reconstructed the Button component.

Create Sign Up Screen

We are going to create a Sign-Up Form for our application with validation. This is how our Signup screen going to look alike.

React Native Signup Form

Let’s start to create the signup screen by adding the following dependencies and components to the App.js file.

import { StatusBar } from 'expo-status-bar';
import React from 'react';
import { StyleSheet, View } from 'react-native';

import InputField from './components/InputField'
import InputButton from './components/InputButton'
import Heading from './components/Heading'

Next, add some style to manage the Signup form UI.

const styles = StyleSheet.create({
    container: {
        flex: 1,
        backgroundColor: '#FAFBFE',
        alignItems: 'center',
        justifyContent: 'center',
    },
    heading: {
        color: '#533263', 
        fontWeight: 'bold'
    },
    form: {
        width: '90%',
        marginTop: 50
    },
});

Now, let’s work on the main form. First, add a container with the help of View (Native component) that holds the entire screen. Set the title “Signup Form” with the help of the Heading component under the parent View component.

export default function App() {
    return (
        <KeyboardAvoidingView style={styles.avoidKeyboard} behavior="padding" enabled keyboardVerticalOffset={85}>
        <View style={styles.container}>
            <Heading h2 title="Signup Form" />
        </View>
        </KeyboardAvoidingView>
    )
}

KeyboardAvoidingView: We are using this component to avoid view to overlap with keyboard

The next step is to create a form container that holds the entire form design that we are going to create through with the help of InputField and the InputButton component. We are going to add the Fullname, Email and Password fields for this Signup form.

<View style={styles.form}>
    <InputField 
        name='fullname'
        label='Full Name'
        placeholder='Enter your name'
        autoCapitalize='none'
        iconName='ios-person'
        iconColor='#533263'
    />

    <InputField 
        name='email'
        label='Email Address'
        placeholder='Enter email'
        keyboardType='email-address'
        returnKeyType='done'
        autoCapitalize='none'
        iconName='ios-mail'
        iconColor='#533263'
    />

    <InputField 
        name='password'
        label='Password'
        placeholder='Enter password'
        autoCapitalize='none'
        secureTextEntry={true}
        iconName='ios-lock'
        iconColor='#533263'
    />

    <InputButton 
        buttonType='solid'
        title='SIGN UP'
        buttonColor='#533263'
    />
</View>

Add Formik to Signup Form

We have completed the Form design. Now its time to implement the form validation process. We are going to use the Formik for form validation. We need to import the Formik to our App.js file.

import { Formik } from 'formik'

Now it’s time to define the initial values and onSubmit event handler.

<Formik
    initialValues={{ 
        fullname: '',
        email: '', 
        password: '' 
    }}
    onSubmit={values => {}}>
    {() => (
        <>
            ....
            ....
            ....
        </>
    )}
</Formik>

As you see in the above codes, we have defined three initial values that are corresponding to the FullName, Email, and Password fields. The onSubmit event is mapped with the handleSubmit event through Formik. We are going to learn about it to the next section.

Now let’s return our form through Formik. Through this, we will have access to the pre-defined Formik properties such as handleChange, values, handleSubmit, etc.

{({handleChange, values, handleSubmit}) => (
    <>
        <InputField 
            name='fullname'
            label='Full Name'
            value={values.fullname}
            onChangeText={handleChange('fullname')}
            placeholder='Enter your name'
            autoCapitalize='none'
            iconName='ios-person'
            iconColor='#533263'
        />
        ....
        ....
        <InputButton 
            onPress={handleSubmit}
            buttonType='solid'
            title='SIGN UP'
            buttonColor='#533263'
        />
    </>
)}

As you can see to the above codes, we have used the values.fullname with the InputField value property that helps to keep updated data for the particular field. Same we used the handleChange event with onChangeText event handler to keep the updated value during the input for the particular state i.e. fullname.

The InputButton event is simply straight forwarded and it works with the onPress that is mapped with handleSubmit Formik event property.

Validate React Native Form With Yup

The next step is to define the validation rules for our initialValues that we have defined with the Formik package. We are going to use the Yup package and let’s import it to the App.js file.

import * as Yup from 'yup'

Next, let’s add the predefined rules to the initialValues i.e. fullname, email and password.

const validationSchema = Yup.object().shape({
    fullname: Yup.string()
        .label('Fullname')
        .required('Please enter your name'),
    email: Yup.string()
        .label('Email')
        .email('Enter a valid email')
        .required('Please enter your email address'),
    password: Yup.string()
        .label('Password')
        .required('Please enter your password')
        .min(4, 'Password must have at least 4 characters ')
})

Formik has a property i.e. validationSchema that accepts schema that could be Yup schema or a function that returns the Yup schema. We are going to use the property to map with Yup validation schema. Through this property, the returned errors are automatically mapped to the inner component i.e. errors.

<Formik
    initialValues={{ 
        fullname: '',
        email: '', 
        password: '' 
    }}
    onSubmit={values => {}}
    validationSchema={validationSchema}>
{({handleChange, values, handleSubmit, errors}) => (
    <>
        ....
        ....
        ....
        ....
    </>
)}
</Formik>

Create a component for error message

Time to create another component to handle errors that match our design we name it ErrorMessage.

import React from 'react'
import { View, Text, StyleSheet } from 'react-native'

const ErrorMessage = ({ errorValue }) => (
    <View style={styles.container}>
        <Text style={styles.errorText}>{errorValue}</Text>
    </View>
)

const styles = StyleSheet.create({
    container: {
        marginLeft: 10
    },
    errorText: {
        color: 'red'
    }
})

export default ErrorMessage

This is how we used our ErrorMessage component to handle the given errors by Yup validation schema.

<>
    <InputField 
        name='fullname'
        label='Full Name'
        value={values.fullname}
        onChangeText={handleChange('fullname')}
        placeholder='Enter your name'
        autoCapitalize='none'
        iconName='ios-person'
        iconColor='#533263'
    />
    <ErrorMessage errorValue={errors.fullname} />
    ....
    ....
    <InputButton 
        onPress={handleSubmit}
        buttonType='solid'
        title='SIGN UP'
        buttonColor='#533263'
    />
</>

Disable Button Until Form Not Valid

Let’s move to the next step to disable the button until the form not valid and during the submitting process. We are going to use other Formik properties i.e. isValid and isSubmitting. Both properties will return the boolean value for the particular action. Like isValid returns true if the form validation complete otherwise it returns false. This is how we can use it to our InputButton component with the disabled property.

{({handleChange, values, handleSubmit, errors, isValid, isSubmitting}) => (
    <>
        ....
        ....
        ....
        ....
        <InputButton 
            onPress={handleSubmit}
            disabled={! isValid || isSubmitting}
            buttonType='solid'
            title='SIGN UP'
            buttonColor='#533263'
        />
    </>
)}

Display Error For Specified Field

Previously, we have added the ErrorMessage component to display the error in our React Native application. But it’s a little embarrassing to display all the errors at the same time. We can display the error message for the specified filed instead of all at once.

We are going to use the touched and handleBlur Formik properties. The touched property contains the initial values as property and returns boolean during the active field. The handleBlur event handler is going to use with onBlur event. Take a look to the below codes.

{({handleChange, values, handleSubmit, errors, isValid, isSubmitting, touched, handleBlur}) => (
    <>
        <InputField 
            name='fullname'
            label='Full Name'
            value={values.fullname}
            onChangeText={handleChange('fullname')}
            onBlur={handleBlur('fullname')}
            placeholder='Enter your name'
            autoCapitalize='none'
            iconName='ios-person'
            iconColor='#533263'
        />
        <ErrorMessage errorValue={touched.fullname && errors.fullname} />
        ....
        ....
        <InputButton 
            onPress={handleSubmit}
            disabled={! isValid || isSubmitting}
            buttonType='solid'
            title='SIGN UP'
            buttonColor='#533263'
        />
    </>
)}

Display Loading Indicator While Submiting

The final step is to add the loading indicator to our screen. We already have the isSubmitting Formik property that invokes with the onSubmit event.

<InputButton 
    onPress={handleSubmit}
    disabled={! isValid || isSubmitting}
    buttonType='solid'
    title='SIGN UP'
    buttonColor='#533263'
    loading={ isSubmitting }
/>

We hope this article will help you to implement form validation in your React Native application. If you like this article then please follow us on Facebook and Twitter.