Building a Newsletter in React with Formik, Yup, and EmailJS

Wynne GitauWynne Gitau
9 min read

There is something about adding a newsletter subscription section to your website that just changes everything about your site. If you are a React developer and you would like a simple way to allow your users to contact you, stick around because we will be doing just that in this tutorial.

To achieve this, we will be working with forms. I know that we all have our grim histories with forms so we will be using these amazing libraries that make form handling a little bit more bearable;

  1. Formik - this will handle the form states (so no more onChange={handleName}, validation and error messages and most importantly, form submissions - did you just say phew! Formik is a form management library that handles the overall process of handling forms in React applications.

  2. Yup - this is a library that will be responsible for enforcing and validating form inputs. So with this baby, we will be able to define validation rules such as the maximum length of an input or to check the data type of an input, numeric ranges etc.

These two will enable us to create robust forms that will enable us to be in control of what the user enters.

What is EmailJS?

EmailJS is a JavaScript library that provides the functionality of sending emails using front-end applications. It is very simplistic in its approach and the best part is that you can fetch as much information from the user as you want. This is made possible by an email template that you will fill out with the variables you want to capture from the user. To successfully make use of Emailjs, we will follow these three easy steps;

  • add an email service - for this tutorial, we will use 'Gmail' - although you can pick from the extensive list of supported services that emails provide; the process is the same.

  • create an email template

  • using Emailjs SDK, we will send form details to the website's owner/developer (you)

    I should mention, emails are free for development purposes and it gives you a monthly quota of 200 email-sends per month. This comes with an API key which means you will have to be logged in to get it.

Getting started

First, let's do our installations with npm. I will assume that you know how to create a React application since what we are building works best when it is part of a project. Below, we will install formik, yup and Emailjs.

Installations

npm i formik yup @emailjs/browser --save

Creating a Form in React

So we are going to go ahead and create a simple form that will have three input fields; a name field, an email field and a phone number field. We will also include a submit button of type submit.

import React from 'react'

const ContactUs = () => {
  return (
    <div>
        <form>
            <label htmlFor="user_name">Name</label>
            <input type="text" name="user_name" id="user_name" />

            <label htmlFor="user_email">Email</label>
            <input type="email" user_email="user_email" id="user_email" />

            <label htmlFor="phone">Phone Number</label>
            <input type="tel" name="phone" id="phone" />

            <button type="submit"> Submit </button>
        </form>
    </div>
  )
}

export default ContactUs;

Above is how you'd normally go about creating a form which you'd then import React.useState() to handle it. Let's see how we'd do it with Formik.

Form Handling with Formik

We will first import the necessary libraries into our react component.

import React, { useRef } from 'react';
import emailjs from '@emailjs/browser';
import {useFormik} from "formik";
import * as Yup from "yup";

The useFormik hook is a function provided by the Formik library that enables you to manage form states, handle form submissions, and perform form validation in React functional components.

export const ContactUs = () => {
const Formik = useFormik({
        initialValues:{
            name:"",
            email:"",
            phone:"",
            message:"",
        }
    })
  return (...)

Above we have defined;

  1. Initial values - You can define the initial values of your form fields by passing an object of key-value pairs as the initialValues. I will iniate the intial values to a variable Formik but you can call it whatever you want.

  2. Form validation using validationSchemas - this is where we will integrate formik and Yup and validate user inputs.

  3. Error handling - formik will track validation errors and provides an errors object that you can use to display error messages or conditionally style elements based on the validation state.

Yup and Formik

export const ContactUs = () => {
const Formik = useFormik({
        initialValues:{
            name:"",
            email:"",
            phone:"",
            message:"",
        },
        // Yup integrates with formik to handle form validation
        validationSchema:Yup.object({
            name:Yup.string().max(30, "name must be 30 characters or less").required("Name is required"),
            email:Yup.string().email("Invalid email address").required("email is required"),
            phone:Yup.number("invalid input")        }),
        onSubmit: (values, {resetForm}) => {
            alert(values)
            resetForm()
        }
    })
  return (...)

We have added a validation schema and used Yup.object({ ... }) to create the schema. This process will create a Yup schema object that will contain the validation rules for the form fields. By defining the validationSchema with Yup, Formik can use these validation rules to automatically validate the form fields. Any validation errors will be stored in the formik.errors object, which can be accessed and displayed to the user if needed.

Note - that in the form we have set the name and email inputs to 'required' so the form will not be submitted without them being filled.

onSubmit Callback

The onSubmit function is a callback that is executed when the form is submitted. it is taking in two parameters, values and resetForm() method. The values are the input data that the user puts in and the method resetForm() method sets all the inputs to an empty string.

Event Handlers

We can then go ahead and link this up to our form. A point to keep in mind is to make sure that the value of the name prop in each input field matches the initialValues otherwise our form won't work.

...
 return (
    <div>
        <form>
        <label htmlFor="user_name" className={`${(Formik.touched.name && Formik.errors.name ) && "text-red-500"}`}>
                Name
                </label>
            <input type="text" name="name" id="user_name"
            onBlur={Formik.handleBlur}
            onChange={Formik.handleChange} 
            value={Formik.values.name} />

            <label htmlFor="email" className={`${(Formik.touched.email && Formik.errors.email ) && "text-red-500"}`}>Email</label>
            <input type="email" name="email" id="user_email"
            onBlur={Formik.handleBlur}
            onChange={Formik.handleChange} 
            value={Formik.values.email} />

            <label htmlFor="phone">Phone Number</label>
            <input type="tel" name="phone" id="phone" 
            onBlur={Formik.handleBlur}
            onChange={Formik.handleChange} 
            value={Formik.values.phone}/>

            <button type="submit">Submit</button>
        </form>
    </div>
  )
}

The helper methods that formik provides to handle the form that we are going to make use of in this tutorial are;

  • handleSubmit - to take care of form submission for us. Remember e.preventDefault(), you won't have to worry about that anymore because every time you submit, you won't get a page refresh.

  • handleChange - this will be applied to the onChange prop and it is used to take care of the inputs.

  • handleBlur - When a user interacts with a form field, such as entering or leaving an input, the handleBlur function can be called to update the field's touched state and trigger any necessary validation checks. It works similarly to the :visited pseudo-class (more or less).

  • (Formik.touched.email && Formik.errors.email): This condition checks if the email field has been touched (Formik.touched.email) and if there are any validation errors for the email field (Formik.errors.email).

  • "text-red-500": If the above condition is true, the class name "text-red-500" will be applied.

Therefore, if the email field has been touched and has validation errors, the class "text-red-500" will be applied to the <label> element. This can be used to visually indicate an error state or style the label differently when there are validation issues with the associated input field.

What we have done so far should allow us to have a working form that performs all the appropriate validations. The form cannot be submitted unless the errors have been fixed. Formik simplifies form handling and validation in React by providing a structured approach and handling many common form-related tasks for you.

Add some styling and you'll be able to have something like this.

Sending an Email with EmailjS

You'll first have to create an account with Emailjs then you'll log in.

Your dashboard will look something like this.

Email Services

Once you're in, you will click on the add new service.

A window will open up.

You will select the service you want which will then prompt you to configure it. You will click on Connect Account. After you've done, that you will click on Create Service.

For this tutorial will use "Gmail". You will also get a service_id which we are going to make use of later on. If you did that right, you should see something like this:

Email Templates

You will then navigate to Email Templates and click on Create New Template. This will bring pull up this window that we have to edit.

The tabs we are going to focus on are the Content and Settings tab.

Content Tab

In Content, the trick is simple, we want to create variables (kinda like how we do in Django templates) with double calibrates. These variables will be filled in after our user submits the data.

The values we are going to use in the variables are going to be the ones we used in the name prop of each of our inputs. For example, in the name input, we used the value name, so we are going to use {{name}} in the template as well...and so on.

Following this you can craft your email template however you like.

On the right side, there is a to Email field that you have to fill in with the email you want to be receiving the emails.

Settings Tab

On the Settings tab, we will get the template_id, and template_name and add a reCaptcha to your website to prevent spam (optional).

We are almost through with our tutorial. We will now add this function to our code and pass in a few things...

...
  const sendEmail = (e) => {
    emailjs.sendForm('YOUR_SERVICE_ID', 'YOUR_TEMPLATE_ID', form.current, 'YOUR_PUBLIC_KEY')
      .then((result) => {
          console.log(result.text);
      }, (error) => {
          console.log(error.text);
      });
  };
...

We will replace ('YOUR_SERVICE_ID', 'YOUR_TEMPLATE_ID', form.current, 'YOUR_PUBLIC_KEY') with the proper entries except for form.current which will be pointing to the form you created. This means that you should have a ref={form} in your form.

  • You will find your service id in the Email Services section.

  • You will find your template id in the Email Templates section.

  • To get your public API key, you will click on your user name and in the General tab, you will find your public key. Note:- You should store the key in a .env file if your want to make your project public otherwise someone else might use your quotas.

Once that is done, we will add the sendEmail() function to the onSubmit function we created in useFormik(...)

import React, { useRef } from 'react';
import emailjs from '@emailjs/browser';
import {useFormik} from "formik";
import * as Yup from "yup";

export const ContactUs = () => {
    const formik = useFormik({
        initialValues:{
            name:"",
            email:"",
            phone:""
        },
        validationSchema:Yup.object({
            name:Yup.string().max(30, "name must be 30 characters or less").required("Name is required"),
            email:Yup.string().email("Invalid email address").required("email is required"),
            phone:Yup.number("invalid input")     
        }),
        onSubmit: (values, {resetForm}) => {
            // alert(values)
            sendEmail()
            resetForm()


        }
    })

  const form = useRef();

  const sendEmail = (e) => {
    emailjs.sendForm('handsofhope', 'handsofhope', form.current, `${process.env.REACT_APP_EMAILJS_KEY}`)
      .then((result) => {
          console.log(result.text);
      }, (error) => {
          console.log(error.text);
      });
  };

  return (
    <form ref={form} onSubmit={formik.handleSubmit}
...)

The resetForm() method will clear our form once submitted.

So now, when we hit the submit button, we should be able to get an email with the form information.

Conclusion

Formik, yup and Emailjs are the perfect combo to use to get a server-side experience in a React application. You can fetch and validate user data in just one component.

0
Subscribe to my newsletter

Read articles from Wynne Gitau directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Wynne Gitau
Wynne Gitau

I am a web developer who leverages the power of GIS in web development. Exploring the intersection of React and innovation. Join me on a tech-filled adventure as we exchange ideas, insights, and code at The Wayne Exchange.