DEV Community

Daryl Lukas
Daryl Lukas

Posted on

Form Validation in React Native using Formik

Form validation is one of the most important tasks when building an app and a form in particular. It’s one of the most tedious ones as well. Here is a unified approach to handling form validation using a neat library called Formik.

Formik is an open-source library for React and React Native that allows you to easily build forms. It takes care of repetitive and annoying tasks such as handling validation and submission, and keeping track of values, errors, and visited or "touched" fields.

The implementation in this guide is done in React Native. But the approach can easily be translated/ported to a React app.

Let's get started.

Project Setup

Dependency overview

  • Expo: Expo is a free and open source toolchain built around React Native to help you build native iOS and Android projects using JavaScript and React. Expo is a great way to get started with React Native.
  • UI Kitten: UI Kitten is a React Native framework for creating stunning cross-platform mobile applications. It is based on Eva Design System and provides a set of general purpose UI components styled in a similar way.
  • Formik
  • Yup: A simple object validation library that works perfectly with Formik.

Initialize Project

We'll start by installing (updating) Expo and initializing a new project

npm i -g expo-cli
npx create-expo-app rn-formik-example
Enter fullscreen mode Exit fullscreen mode

Install the dependecies

Navigate to the application and install these dependencies.

cd rn-formik-example
yarn add @ui-kitten/components @eva-design/eva formik yup
npx expo install react-native-svg@9.13.6
Enter fullscreen mode Exit fullscreen mode

Configure UI Kitten

Wrap the root component App with ApplicationProvider from the @ui-kitten/components library.

import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Text, View } from 'react-native';
import * as eva from '@eva-design/eva';
import { ApplicationProvider } from '@ui-kitten/components';

export default function App() {
  return (
    <ApplicationProvider {...eva} theme={eva.light}>
      <View style={styles.container}>
        <Text>Open up App.js to start working on your app!</Text>
        <StatusBar style="auto" />
      </View>
    </ApplicationProvider>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});
Enter fullscreen mode Exit fullscreen mode

Design the Form

Using UI Kitten components, let's create a simple registration form component called Register in src/screens/Register.component.js

import React from 'react';
import { Button, Input, Text } from '@ui-kitten/components';
import { ScrollView, StyleSheet, View } from 'react-native';

export const Register = () => {
  return (
    <ScrollView>
      <View style={styles.container}>
        <Input
          label="First Name"
          placeholder="First Name"
          size="large"
          style={styles.formCtrl}
        />
        <Input
          label="Last Name"
          placeholder="Last Name"
          size="large"
          style={styles.formCtrl}
        />
        <Input
          label="Email Address"
          placeholder="name@example.com"
          size="large"
          style={styles.formCtrl}
        />
        <Input
          label="Phone Number"
          accessoryLeft={() => <Text>+260</Text>}
          placeholder="971234567"
          size="large"
          style={styles.formCtrl}
        />
        <Input
          label="Password"
          placeholder="Password"
          size="large"
          style={styles.formCtrl}
          secureTextEntry
        />
        <Input
          label="Confirm Password"
          placeholder="Confirm Password"
          size="large"
          style={styles.formCtrl}
          secureTextEntry
        />
        <Button style={styles.submitBtn}>Register</Button>
      </View>
    </ScrollView>
  );
};

const styles = StyleSheet.create({
  container: {
    paddingVertical: 50,
    paddingHorizontal: 20,
  },
  formCtrl: {
    marginBottom: 30,
  },
  submitBtn: {
    margin: 10,
  },
});
Enter fullscreen mode Exit fullscreen mode

Update App.js to display Register

// Truncated
...
import { Register } from './src/screens/Register.component';

export default function App() {
  return (
    <ApplicationProvider {...eva} theme={eva.light}>
      <Register />
    </ApplicationProvider>
  );
}
...
// Truncated
Enter fullscreen mode Exit fullscreen mode

You app should look like this.

React Native form using UI Kittten

Create Validation Schema

Using Yup, we'll create a validation schema for the form. Create a new file src/schemas/register.form.js and add the following code

import * as yup from 'yup';

export const registerSchema = yup.object().shape({
  firstName: yup.string().required('First Name is required'),
  lastName: yup.string().required('Last Name is required'),
  email: yup.string().email('Invalid email').required('Email is required'),
  phoneNumber: yup
    .string()
    .matches(/^[0-9]+$/, 'Must be only digits')
    .min(9, 'Must be exactly 9 digits')
    .max(9, 'Must be exactly 9 digits')
    .required('Phone Number is required'),
  password: yup
    .string()
    .min(8, ({ min }) => `Password must be at least ${min} characters`)
    .required('Password is required'),
  confirmPassword: yup
    .string()
    .oneOf([yup.ref('password'), null], 'Passwords must match')
    .required('Confirm Password is required'),
});

export const registerInitialValues = {
  firstName: '',
  lastName: '',
  email: '',
  phoneNumber: '',
  password: '',
  confirmPassword: '',
};
Enter fullscreen mode Exit fullscreen mode

Yup's API is pretty easy to understand and use. For example, the firstName field in the schema is mapped to required string schema, yup.string().required('First Name is required'). The form is invalid if no value is given for this field. Yup also allows you to change multiple validation rules for a single field. For example, the phoneNumber field has multiple validation rules such as, matches, min, max, and required. Check out the Yup API documentation for more details.

Add Formik to the Form

The simplest way to add Formik to the form is by using the useFormik hook. Update src/screens/Register.component.js to use the hook

...
import { useFormik } from 'formik';
import { registerInitialValues, registerSchema } from '../schemas/register.form';

export const Register = () => {
  const formik = useFormik({
    initialValues: registerInitialValues,
    validationSchema: registerSchema,
    onSubmit: (values) => {
      console.log(values);
      Alert.alert(
        `Welcome, ${values.firstName}`,
        'Your account has been created.'
      );
    },
  });

  ...
Enter fullscreen mode Exit fullscreen mode

Update the Input components to handle the formik state for respective fields. For example, the First Name component should look like this

<Input
  label="First Name"
  placeholder="First Name"
  size="large"
  style={styles.formCtrl}
  onChangeText={formik.handleChange('firstName')}
  onBlur={formik.handleBlur('firstName')}
  value={formik.values?.firstName}
/>
Enter fullscreen mode Exit fullscreen mode

Update the Button component to handle the form submission

<Button style={styles.submitBtn} onPress={formik.handleSubmit}>
  Register
</Button>
Enter fullscreen mode Exit fullscreen mode

Add additional props to the Input components to display validation errors

<Input
  label="First Name"
  placeholder="First Name"
  size="large"
  style={styles.formCtrl}
  onChangeText={formik.handleChange('firstName')}
  onBlur={formik.handleBlur('firstName')}
  value={formik.values?.firstName}
  status={
    formik.touched.firstName && Boolean(formik.errors.firstName)
      ? 'danger'
      : 'basic'
  }
  caption={formik.touched.firstName && formik.errors.firstName}
/>
Enter fullscreen mode Exit fullscreen mode

Do this for the rest of the Input components.

The Register component should look like this

import React from 'react';
import { Button, Input, Text } from '@ui-kitten/components';
import { ScrollView, StyleSheet, View } from 'react-native';
import { useFormik } from 'formik';
import {
  registerInitialValues,
  registerSchema,
} from '../schemas/register.form';

export const Register = () => {
  const formik = useFormik({
    initialValues: registerInitialValues,
    validationSchema: registerSchema,
    onSubmit: (values) => {
      console.log(values);
      Alert.alert(
        `Welcome, ${values.firstName}`,
        'Your account has been created.'
      );
    },
  });

  return (
    <ScrollView>
      <View style={styles.container}>
        <Input
          label="First Name"
          placeholder="First Name"
          size="large"
          style={styles.formCtrl}
          onChangeText={formik.handleChange('firstName')}
          onBlur={formik.handleBlur('firstName')}
          value={formik.values?.firstName}
          status={
            formik.touched.firstName && Boolean(formik.errors.firstName)
              ? 'danger'
              : 'basic'
          }
          caption={formik.touched.firstName && formik.errors.firstName}
        />
        <Input
          label="Last Name"
          placeholder="Last Name"
          size="large"
          style={styles.formCtrl}
          onChangeText={formik.handleChange('lastName')}
          onBlur={formik.handleBlur('lastName')}
          value={formik.values.lastName}
          status={
            formik.touched.lastName && Boolean(formik.errors.lastName)
              ? 'danger'
              : 'basic'
          }
          caption={formik.touched.lastName && formik.errors.lastName}
        />
        <Input
          label="Email Address"
          placeholder="name@example.com"
          size="large"
          style={styles.formCtrl}
          onChangeText={formik.handleChange('email')}
          onBlur={formik.handleBlur('email')}
          value={formik.values.email}
          status={
            formik.touched.email && Boolean(formik.errors.email)
              ? 'danger'
              : 'basic'
          }
          caption={formik.touched.email && formik.errors.email}
        />
        <Input
          label="Phone Number"
          accessoryLeft={() => <Text>+260</Text>}
          placeholder="971234567"
          size="large"
          style={styles.formCtrl}
          onChangeText={formik.handleChange('phoneNumber')}
          onBlur={formik.handleBlur('phoneNumber')}
          value={formik.values.phoneNumber}
          status={
            formik.touched.phoneNumber && Boolean(formik.errors.phoneNumber)
              ? 'danger'
              : 'basic'
          }
          caption={formik.touched.phoneNumber && formik.errors.phoneNumber}
        />
        <Input
          label="Password"
          placeholder="Password"
          size="large"
          style={styles.formCtrl}
          onChangeText={formik.handleChange('password')}
          onBlur={formik.handleBlur('password')}
          value={formik.values.password}
          status={
            formik.touched.password && Boolean(formik.errors.password)
              ? 'danger'
              : 'basic'
          }
          caption={formik.touched.password && formik.errors.password}
          secureTextEntry
        />
        <Input
          label="Confirm Password"
          placeholder="Confirm Password"
          size="large"
          style={styles.formCtrl}
          onChangeText={formik.handleChange('confirmPassword')}
          onBlur={formik.handleBlur('confirmPassword')}
          value={formik.values.confirmPassword}
          status={
            formik.touched.confirmPassword &&
            Boolean(formik.errors.confirmPassword)
              ? 'danger'
              : 'basic'
          }
          caption={
            formik.touched.confirmPassword && formik.errors.confirmPassword
          }
          secureTextEntry
        />
        <Button style={styles.submitBtn} onPress={formik.handleSubmit}>
          Register
        </Button>
      </View>
    </ScrollView>
  );
};

const styles = StyleSheet.create({
  container: {
    paddingVertical: 50,
    paddingHorizontal: 20,
  },
  formCtrl: {
    marginBottom: 30,
  },
  submitBtn: {
    margin: 10,
  },
});
Enter fullscreen mode Exit fullscreen mode

That's it. You have successfully created a form with validation using Formik.
Run your app to test. Depending on your development environment, you can use one of the following commands:

yarn android # for android
yarn ios # for ios
Enter fullscreen mode Exit fullscreen mode

Conclusion

In this tutorial, you have learned how to create a form with validation in React Native using Formik (and UI Kitten). You have also learned how to use the useFormik hook to handle form state and yup to define validation rules. You can also use the same approach to create forms in your React web applications.

Source Code

Available on GitHub.

New to React?

I'm offering a 1-on-1, pair programming-styled, remote training in React. Apply here.

Top comments (2)

Collapse
 
eminarium profile image
Merdan Durdyyev

Amazing post with good examples, Daryl !

Collapse
 
daryllukas profile image
Daryl Lukas

3
Thank you, Merdan!