How to Validate Emails in React Native

On November 15, 2023
12min read
Ketevan Bostoganashvili Technical Content Writer @Mailtrap
Artur Hebda Full Stack Developer @Railsware

In this guide, we’ll explore how to set up email validation in React Native. We’ll cover methods such as Regex, the Validator package, the combination of React Hook Form and Validator, the combination of Formik and Yup, and custom validation. 

Continue reading to learn more about these methods with relevant code examples. 

Email syntax validation vs. email address validation

Before we get started, there are two things to keep in mind: 

  • Our primary focus for this blog post will be syntax validation, which means making sure the format of the entered email address matches the standard email syntax rules. 
  • Sometimes email validation is used in the context of determining if an email address is valid. For that, you’ll need specific tools. We’ll be listing some of them below

Email validation using Regex – some considerations 

An email address consists of two parts – local and domain. The local part may contain alphanumeric characters, punctuation symbols, and special characters ($, %, etc.). At the same time, it cannot start and end with a full stop or dot. The dot can’t be used consecutively like sam..williams@example.com. 

The domain part is based entirely on alphanumeric characters. Hyphens are allowed if they are not the first or last character. Similar to the dot, the hyphen cannot be used consecutively either. 

When you know the validation rules, you’ll be able to implement them in a regular expression or regex. That would be the best option for email validation. The thing is, there is no universal validation regex. You can use an RFC 5322-compliant regex like the one below: 

(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])

Also, you can browse online to find some basic examples such as:

  •  /\S+@\S+/ - checks whether the input email address consists of “something”, an @ symbol, and “something else”
  • ^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$ – catches most valid email addresses.

Complicated and accurate regex are also available, but they might be too difficult to understand. For example, check out this advanced regex implemented via the validation function:

const validate = (email) => {
    const expression = /(?!.*\.{2})^([a-z\d!#$%&'*+\-\/=?^_`{|}~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+(\.[a-z\d!#$%&'*+\-\/=?^_`{|}~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+)*|"((([\t]*\r\n)?[\t]+)?([\x01-\x08\x0b\x0c\x0e-\x1f\x7f\x21\x23-\x5b\x5d-\x7e\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|\\[\x01-\x09\x0b\x0c\x0d-\x7f\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))*(([\t]*\r\n)?[\t]+)?")@(([a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|[a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF][a-z\d\-._~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]*[a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])\.)+([a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|[a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF][a-z\d\-._~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]*[a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])\.?$/i;

    return expression.test(String(email).toLowerCase())
}

Some say that validating email addresses with regex is a waste of time. For example:

Some people, when confronted with a problem, think, ‘I know, I’ll use regular expressions.’ Now they have two problems.

Jamie Zawinski Programmer and Blogger @JWZ

If you try regex for validating emails, there will always be at least one user whose otherwise perfect email address is invalid in your app. 

Email validation in React Native using JS libraries 

The cool thing about React Native is that you can choose from plenty of JavaScript libraries with the validation logic already implemented. We’ll break down the most worthwhile options below.

Validator package

We’ll start with the Validator.js package. It’s a library that validates and sanitizes strings. 

To install the package, run the npm i validator command. 

Then, paste the following script into your project. 

import React, { useState } from 'react';
import { StyleSheet, Text, View, TextInput } from 'react-native';
import validator from 'validator';
export default function App() {
// State to hold the email input
const [email, setEmail] = useState('');
// State to hold any validation error messages
const [error, setError] = useState(null);
// Function to validate the entered email
const validateEmail = (input) => {
// Update the email state with the input
setEmail(input);
// Check if the email input is empty
if (!input) {
setError('Email is required.');
return;
}
// Check if the input is a valid email using the validator library
if (validator.isEmail(input)) {
setError('');
} else {
setError('Please enter a valid email address.');
}
};
return (
<View style={styles.container}>
<Text>Email:</Text>
<View style={styles.inputContainer}>
<TextInput
style={styles.input}
value={email}
// On change of text, validate the email
onChangeText={validateEmail}
placeholder="Enter your email"
/>
{/* Display the error/validation markers if applicable */}
{error !== null && (
error ? (
<Text style={styles.invalidMark}>✗</Text>
) : (
<Text style={styles.validMark}>✓</Text>
)
)}
</View>
{/* Display the error message if there is one */}
{error ? <Text style={styles.errorMessage}>{error}</Text> : null}
</View>
);
}
// Styles for the app components
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingHorizontal: 20,
},
inputContainer: {
flexDirection: 'row', // Align children horizontally
alignItems: 'center', // Align children vertically in the center
},
input: {
flex: 1, // Take all available horizontal space
height: 40,
borderColor: 'gray',
borderWidth: 1,
marginTop: 10,
marginBottom: 10,
paddingLeft: 10,
},
errorMessage: {
color: 'red',
},
validMark: {
color: 'green',
marginLeft: 5,
fontSize: 20,
},
invalidMark: {
color: 'red',
marginLeft: 5,
fontSize: 20,
}
});

Here, we use validateEmail function to validate the emails. It takes only one argument, which is input. This argument represents the email address entered by the user. 

The validation begins immediately when the user starts typing in the email address field. This is done with the help of onChangeText prop of the TextInput component. The function also checks for empty inputs and displays an error message. Finally, Validator.isEmail checks if the input is a valid email. 

This script also includes the styling section, which isn’t necessary for input functionality. However, it adds basic aesthetics to the input field. 

React Hook Form + Validator

The next option is the combination of React Hook Form and Validator.js packages. This approach reduces the boilerplate required to manage form states and validation, while still leveraging the features of the validator package. 

Here’s how you can build an email validation script with react hook form and validator. 

1. Install the package using the following command: 

npm i react-hook-form validator

2. Prepare the necessary imports. 

import React from 'react';
import { StyleSheet, Text, View, TextInput } from 'react-native';
import { useForm, Controller } from 'react-hook-form';
import validator from 'validator';

3. Initialize the form control using the useForm hook and prepare the formState to hold the form state including any errors. Add the trigger function to trigger validation manually for individual fields. 

const { control, formState: { errors }, trigger } = useForm({
defaultValues: {
email: '',
},
});

4. Then, use validateEmail and validator.isEmail to check if the input is empty and if the provided email address is valid. If the email address is valid, the code should return true. If not, it will return an error message. 

const validateEmail = (input) => {
if (!input) return 'Email is required.'; // Check if email input is empty
if (!validator.isEmail(input)) return 'Please enter a valid email address.'; // Check if the email format is valid
return true;
};

5. Define the render method. Use the Controller to integrate input with react-hook-form and define validation rules. 

return (
<View style={styles.container}>
<Text>Email:</Text>
<View style={styles.inputContainer}>
{/* Use Controller to integrate input with react-hook-form */}
<Controller
control={control}
rules={{ validate: validateEmail }} // Define validation rules
render={({ field: { onChange, onBlur, value } }) => (
<>

6. Use conditional rendering to display validation marks based on input value and errors. It will display a checkmark for valid inputs and an X for invalid inputs. 

{/* Display validation marks based on input value and errors */}
{value ? (
!errors.email ? (
<Text style={styles.validMark}>✓</Text> // Display a checkmark for valid input
) : (
<Text style={styles.invalidMark}>✗</Text> // Display an X mark for invalid input
)
) : null}
</>
)}
name="email"
/>
</View>
{/* Display error message for email validation */}
{errors.email && (
<Text style={styles.errorMessage}>{errors.email.message}</Text>
)}
</View>
);
}

7. Finally, add styling for UI components. Once again, this is an optional step that you can skip if you’re building a basic form without any styling elements.

// Styling for UI components
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingHorizontal: 20,
},
inputContainer: {
flexDirection: 'row',
alignItems: 'center',
},
input: {
flex: 1,
height: 40,
borderColor: 'gray',
borderWidth: 1,
marginTop: 10,
marginBottom: 10,
paddingLeft: 10,
},
errorMessage: {
color: 'red',
},
validMark: {
color: 'green',
marginLeft: 5,
fontSize: 20,
},
invalidMark: {
color: 'red',
marginLeft: 5,
fontSize: 20,
},
});

The complete sample should look something like this: 

import React from 'react';
import { StyleSheet, Text, View, TextInput } from 'react-native';
import { useForm, Controller } from 'react-hook-form';
import validator from 'validator';
export default function App() {
// Initialize form control using react-hook-form
const { control, formState: { errors }, trigger } = useForm({
defaultValues: {
email: '',
},
});
// Validation function for email input
const validateEmail = (input) => {
if (!input) return 'Email is required.'; // Check if email input is empty
if (!validator.isEmail(input)) return 'Please enter a valid email address.'; // Check if the email format is valid
return true;
};
return (
<View style={styles.container}>
<Text>Email:</Text>
<View style={styles.inputContainer}>
{/* Use Controller to integrate input with react-hook-form */}
<Controller
control={control}
rules={{ validate: validateEmail }} // Define validation rules
render={({ field: { onChange, onBlur, value } }) => (
<>
{/* Input field for email */}
<TextInput
style={styles.input}
onBlur={onBlur}
onChangeText={async (text) => {
onChange(text); // Update input value
trigger('email'); // Trigger email validation
}}
value={value}
placeholder="Enter your email"
/>
{/* Display validation marks based on input value and errors */}
{value ? (
!errors.email ? (
<Text style={styles.validMark}>✓</Text> // Display a checkmark for valid input
) : (
<Text style={styles.invalidMark}>✗</Text> // Display an X mark for invalid input
)
) : null}
</>
)}
name="email"
/>
</View>
{/* Display error message for email validation */}
{errors.email && (
<Text style={styles.errorMessage}>{errors.email.message}</Text>
)}
</View>
);
}
// Styling for UI components
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingHorizontal: 20,
},
inputContainer: {
flexDirection: 'row',
alignItems: 'center',
},
input: {
flex: 1,
height: 40,
borderColor: 'gray',
borderWidth: 1,
marginTop: 10,
marginBottom: 10,
paddingLeft: 10,
},
errorMessage: {
color: 'red',
},
validMark: {
color: 'green',
marginLeft: 5,
fontSize: 20,
},
invalidMark: {
color: 'red',
marginLeft: 5,
fontSize: 20,
},
});

Formik + Yup

The third most common option is to use the combination of Formik and Yup

To get started, install Formik and Yup with npm i formik yup command. 

Then, use the following script to handle the validation. Similar to previous examples, the validation will begin immediately as the user starts entering data in the input field. 

The script will use Yup for the validation schema and Formik for the form state management and validation. 

import { StyleSheet, Text, View, TextInput } from 'react-native';
import { Formik, ErrorMessage } from 'formik';
import * as Yup from 'yup';
export default function App() {
// Define the validation schema using Yup
const validationSchema = Yup.object().shape({
email: Yup.string()
.email('Please enter a valid email address.') // Error message for invalid email format
.required('Email is required.') // Error message for empty email field
});
return (
// Main container for the app
<View style={styles.container}>
<Text>Email:</Text>
{/* Formik component for handling form state and validation */}
<Formik
validateOnChange={true} // Validate the form when the input value changes
initialValues={{ email: '' }} // Initial form values
validationSchema={validationSchema} // Apply the defined validation schema
>
{/* This is a JavaScript function that takes an object as an argument using destructuring */}
{/* It receives an object with various properties and functions from the Formik context */}
{({ values, handleChange, handleBlur, errors, touched }) => (
<>
{/* Input field container */}
<View style={styles.inputContainer}>
<TextInput
style={styles.input}
onChangeText={handleChange('email')} // Update the email value when text changes
onFocus={handleBlur('email')} // Handle field blur (focus out)
value={values.email} // Bind the input value to the form value
placeholder='Enter your email'
name="email"
/>
{/* Display a checkmark (✓) for valid email and a cross (✗) for invalid email */}
{touched.email ? (
errors.email ? (
<Text style={styles.invalidMark}>✗</Text>
) : (
<Text style={styles.validMark}>✓</Text>
)
) : null}
</View>
{/* Display the error message if email validation fails */}
<ErrorMessage name="email">
{(message) => (
<Text style={styles.errorMessage}>{message}</Text>
)}
</ErrorMessage>
</>
)}
</Formik>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingHorizontal: 20,
},
inputContainer: {
flexDirection: 'row',
alignItems: 'center',
},
input: {
flex: 1,
height: 40,
borderColor: 'gray',
borderWidth: 1,
marginTop: 10,
marginBottom: 10,
paddingLeft: 10,
},
errorMessage: {
color: 'red',
},
validMark: {
color: 'green',
marginLeft: 5,
fontSize: 20,
},
invalidMark: {
color: 'red',
marginLeft: 5,
fontSize: 20,
},
});

Custom validation

The final option is to use custom validation. The code will be similar to the Validator.js, but, this time, we’ll use the custom regex instead of the Validator library. We’ll just have to update the validateEmail function like so: 

const validateEmail = (input) => {
setEmail(input);
if (!input) {
setError('Email is required.');
return;
}
const emailPattern = /^[a-zA-Z0-9._+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,24}$/;
if (emailPattern.test(input)) {
setError('');
} else {
setError('Please enter a valid email address.')
}
};

Best APIs to validate email addresses 

One job is done – you have implemented the email syntax validity in your React Native app. But still, this won’t protect you from users who sign up using disposable email addresses. So what? Temporary email services like Nada can be used as a bypass for abusing freemium features. 

But there’s another problem. If your email campaign contains too many disposable recipients, your sender reputation will decline. As a result, spam filters will be at odds with the messages sent from your domain. 

Wouldn’t it be awesome if your app could detect such dodgers and encourage them to sign up with their personal or business email addresses? The following services provide the solution. You can easily integrate their APIs into your app and close the door to fake users. 

Byteplant’s Email Validator

Byteplant allows you to check the quality of email addresses right at the moment of signup. Email Validator API detects if the input address is valid. So, you’ll be able to filter disposable and fake input data. Email syntax validation is also supported.

Mailgun

Mailgun is a popular email service for developers. Its API provides a three-step validation check and comprehensive email verification. Also, it catches user-entry errors in your sign-up flow.

Trumail (now part of Emailable)

Trumail used to be an independent email validation API, but now it’s part of Emailable. Emailable allows you to integrate email validation API with client libraries for Ruby, Node.js, and Python. It supports batch operations and provides statistics for performed validations. 

How to test emails before sending them from React Native app 

Let’s say you’ve successfully implemented email validation for the sign-up form in your app.

The next step is to send a confirmation message to the email address that users input in the form. In the best-case scenario, this transactional email will appear in the recipient’s inbox. 

However, that’s only the best-case scenario. In a real environment, such emails can easily end up in the spam folder or get discarded if the sender’s reputation is low. To prevent that from happening, you should use a dedicated testing tool, Mailtrap Email Testing

Try Mailtrap for Free

Email Testing is an Email Sandbox that captures all the SMTP traffic from the staging environment and puts your emails into a virtual inbox. It automates testing workflow with flexible APIs, inspects HTML/CSS, and checks emails against spam filters and blacklists. 

Mailtrap Email Testing - Spam Analysis

With Email Testing, you can view HTML source, text or raw data, or expand tech info to validate email headers. 

Mailtrap Email Testing - Tech info

To integrate Email Testing with your React Native app, you should go to mailtrap.io and create a free account. Then you’ll need to create an HTTP request to send emails to the virtual inbox. You can refer to the API documentation for more information. 

To compile an HTTP request, you first need to get your credentials. In the example below, your_inbox_id is just a placeholder. You should substitute it with the actual inbox ID. To get it, simply go to your Mailtrap Email Testing account, click on the inbox where you want to receive the email, and check the URL. 

Mailtrap Email Testing - Inbox ID

Similarly, your_api_token should be substituted with your actual API token. To find it, choose ‘API’ from the left navigation panel, select ‘API Tokens’, press three dots across your domain, and hit ‘Copy token’. 

Alternatively, view ‘My Profile’ and you’ll see your API token under ‘User settings’. 

Here’s the sample HTTP request that will send a multipart email with attachments to a selected inbox:

POST /api/send/your_inbox_id HTTP/1.1
Content-Type: application/json
Api-Token: your_api_token
Host: sandbox.api.mailtrap.io
Content-Length: 1173

{
  "to": [
    {
      "email": "john_doe@example.com",
      "name": "John Doe"
    }
  ],
  "cc": [
    {
      "email": "jane_doe@example.com",
      "name": "Jane Doe"
    }
  ],
  "bcc": [
    {
      "email": "james_doe@example.com",
      "name": "Jim Doe"
    }
  ],
  "from": {
    "email": "sales@example.com",
    "name": "Example Sales Team"
  },
  "attachments": [
    {
      "content": "PCFET0NUWVBFIGh0bWw+CjxodG1sIGxhbmc9ImVuIj4KCiAgICA8aGVhZD4KICAgICAgICA8bWV0YSBjaGFyc2V0PSJVVEYtOCI+CiAgICAgICAgPG1ldGEgaHR0cC1lcXVpdj0iWC1VQS1Db21wYXRpYmxlIiBjb250ZW50PSJJRT1lZGdlIj4KICAgICAgICA8bWV0YSBuYW1lPSJ2aWV3cG9ydCIgY29udGVudD0id2lkdGg9ZGV2aWNlLXdpZHRoLCBpbml0aWFsLXNjYWxlPTEuMCI+CiAgICAgICAgPHRpdGxlPkRvY3VtZW50PC90aXRsZT4KICAgIDwvaGVhZD4KCiAgICA8Ym9keT4KCiAgICA8L2JvZHk+Cgo8L2h0bWw+Cg==",
      "filename": "index.html",
      "type": "text/html",
      "disposition": "attachment"
    }
  ],
  "custom_variables": {
    "user_id": "45982",
    "batch_id": "PSJ-12"
  },
  "headers": {
    "X-Message-Source": "dev.mydomain.com"
  },
  "subject": "Confirmation: Your Account Has Been Created!",
  "text": "Thanks for joining our portal!",
  "category": "API Test"
}

If you did everything correctly, you’ll receive the following code as a response: 

{
  "success": true,
  "message_ids": [
    "0c7fd939-02cf-11ed-88c2-0a58a9feac02"
  ]
}

Full code snippet would look like this:

import React, { Component, createRef } from 'react';
import { Text, View, TouchableHighlight } from 'react-native';
import t from 'tcomb-form-native';
import { validate as validateEmail } from 'email-validator';

const Form = t.form.Form;

const Email = t.refinement(t.String, validateEmail)

const Login = t.struct({
  emailAddress: Email
});

const options = {
  fields: {
    emailAddress: {
      error: 'Please enter a valid email address'
    }
  },
  order: ['emailAddress']
}

export default class MyForm extends Component {

  constructor(props) {
    super(props);
    this.state = { emailAddress: "example@gmail.com" };
    this._onPressButton = this._onPressButton.bind(this);
    this.formRef = createRef();
  }

  _onPressButton() {
    const value = this.formRef.current.getValue();
    if (value) { // if validation fails, value will be null
      this._sendEmail();
    }
  }

  // TODO: replace your_inbox_id and your_api_token with your specific values
  _sendEmail() {
    const data = {
      "to": [
        {
          "email": "john_doe@example.com",
          "name": "John Doe"
        }
      ],
      "cc": [
        {
          "email": "jane_doe@example.com",
          "name": "Jane Doe"
        }
      ],
      "bcc": [
        {
          "email": "james_doe@example.com",
          "name": "Jim Doe"
        }
      ],
      "from": {
        "email": "sender@example.com",
        "name": "Example Sender Account"
      },
      "attachments": [
        {
          "content": "PCFET0NUWVBFIGh0bWw+CjxodG1sIGxhbmc9ImVuIj4KCiAgICA8aGVhZD4KICAgICAgICA8bWV0YSBjaGFyc2V0PSJVVEYtOCI+CiAgICAgICAgPG1ldGEgaHR0cC1lcXVpdj0iWC1VQS1Db21wYXRpYmxlIiBjb250ZW50PSJJRT1lZGdlIj4KICAgICAgICA8bWV0YSBuYW1lPSJ2aWV3cG9ydCIgY29udGVudD0id2lkdGg9ZGV2aWNlLXdpZHRoLCBpbml0aWFsLXNjYWxlPTEuMCI+CiAgICAgICAgPHRpdGxlPkRvY3VtZW50PC90aXRsZT4KICAgIDwvaGVhZD4KCiAgICA8Ym9keT4KCiAgICA8L2JvZHk+Cgo8L2h0bWw+Cg==",
          "filename": "index.html",
          "type": "text/html",
          "disposition": "attachment"
        }
      ],
      "custom_variables": {
        "user_id": "45982",
        "batch_id": "PSJ-12"
      },
      "headers": {
        "X-Message-Source": "dev.mydomain.com"
      },
      "subject": "Test Email",
      "html": "<p>This is a test email sent from a React Native app using Mailtrap Email Sandbox.</p>",
      "category": "API Test"
    }

    return fetch(
      'https://sandbox.api.mailtrap.io/api/send/your_inbox_id',
      {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Api-Token': 'your_api_token'
        },
        body: JSON.stringify(data)
    }).then(response => response.json())
      .then(data => console.log('Email sent: ' + data))
      .catch(error => console.error(error));
  }

  render() {
    return (
      <View>
        <Form ref={this.formRef} type={Login} value={this.state} options={options} />
        <TouchableHighlight onPress={this._onPressButton}>
          <Text>Submit</Text>
        </TouchableHighlight>
      </View>
    )
  }
}

Note: The code sample above will work without limitations from the mobile app built with React Native. However, you may run into some limitations if you use it from the browser.

Since testing is made simple with Mailtrap Email Testing, you should always take this step after validating emails in React Native.

Article by Ketevan Bostoganashvili Technical Content Writer @Mailtrap

I’m a Technical Content Writer with more than 5 years of experience and passion to cover software engineering topics. I mostly write about email infrastructure and create code-rich guides about sending and testing emails, but I also love writing about result-driven email marketing.

Article by Artur Hebda Full Stack Developer @Railsware

Comments

1 replies

Evans

Reacher (https://reacher.email) is a good alternative to trumail (which is discontinued). It’s written in Rust, 100% open-source, and free for personal use.

Comments are closed.