Logo

dev-resources.site

for different kinds of informations.

Simple Next.JS Form to Email Using React-Hook-Form and Gmail

Published at
11/9/2022
Categories
nextjs
nodemailer
Author
designly
Categories
2 categories in total
nextjs
open
nodemailer
open
Author
8 person written this
designly
open
Simple Next.JS Form to Email Using React-Hook-Form and Gmail

There are many paid form handling API services out there. Some service have free-tier accounts, but are very limited. But who wants to pay for the simple function of sending a form to an email address?

If all you want to do is collect information from a website user and email it to yourself and also send a confirmation email to the user, and you don't need any database storage, then this tutorial is for you!

This tutorial is part two of a series on creating web forms using Next.JS, React-Hook-Form, Yup and Bootstrap. You'll want to read the first article, Next.JS - Kick-Ass Form Handling Using React-Hook-Form, Yup and Bootstrap, before proceeding.

Step 1 - Set Up Our Environment

First we need to add a couple additional packages to our project to handle the back-end of our form.

npm install nodemailer axios
Enter fullscreen mode Exit fullscreen mode

Now, to use your Gmail account as a form mailer, you'll need to log in to your Google account and set up an App Password for the back-end API to use. If you're unsure how to do this, check out This Tutorial.

Next, let's set up our local dev environment. Create a file in the root directory called .env.local:

NEXT_PUBLIC_BASE_URL=http://localhost:3000
[email protected]
NEXT_PUBLIC_GMAIL_PASS=your_gmail_app_password
Enter fullscreen mode Exit fullscreen mode

Step 2 - Create Our Back-End Form Handler

Ok, the first thing we need to do is create a back-end API endpoint to handle our form data and send it to our gmail account.

Create a folder in the public folder called email-templates. These templates will be used by the API backend. Each email will have an HTML and Plaintext version.

template.html:

This file will be used by all email templates.

<!DOCTYPE html>
<html>

<head>
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;600&display=swap" rel="stylesheet">
    <style>
        body {
            background-color: #171717;
            color: #d8d8d8;
            font-family: 'Montserrat', sans-serif;
            padding: 1em;
        }
    </style>
</head>

<body>
%BODY%
</body>

</html>
Enter fullscreen mode Exit fullscreen mode

admin.html:

<h1>New Message From Website</h1>
<p>The following information was submitted:</p>
<p>Name: %NAME%</p>
<p>Email: %EMAIL%</p>
<p>Message:</p>
<p>%MESSAGE%</p>
Enter fullscreen mode Exit fullscreen mode

admin.txt:

New Message From Website

The following information was submitted:

Name: %NAME%
Email: %EMAIL%
Message:
%MESSAGE%
Enter fullscreen mode Exit fullscreen mode

customer.html:

<h1>We Received Your Message!</h1>
<p>Dear %NAME%:</p>
<p>Thank you for inquiring. We just wanted to let you know that we've received your message and will be responding soon.
</p>
<p>Have a great day!</p>
<p>The Webmaster</p>
Enter fullscreen mode Exit fullscreen mode

customer.txt:

Message Received!

Dear %NAME%:

Thank you for inquiring. We just wanted to let you know that we've received your message and will be responding soon.

Have a great day!

- The Webmaster
Enter fullscreen mode Exit fullscreen mode

Now let's create a file called contact-form.js in the /pages/api/ directory:

import axios from 'axios';

const nodemailer = require("nodemailer");

// Config
const mailConfig = {
    host: "smtp.gmail.com",
    port: 465, // or 587
    secure: true, // true for 465, false for other ports
    auth: {
        user: process.env.NEXT_PUBLIC_GMAIL_USER, // your gmail account
        pass: process.env.NEXT_PUBLIC_GMAIL_PASS // your gmail app password
    }
}

const adminEmail = 'The Webmaster <[email protected]>';

// Function for grabbing template files
async function getPubFile(file) {
    const res = await axios.get(`${process.env.NEXT_PUBLIC_BASE_URL}${file}`);
    return res.data;
}

export default async function handler(req, res) {
    sendEmails(req, res);
}

async function sendEmails(req, res) {
    // Create our Nodemailer transport handler
    let transporter = nodemailer.createTransport(mailConfig);

    // Fetch our template files
    const template = await getPubFile("/email-templates/template.html");
    const custHtml = await getPubFile("/email-templates/customer.html");
    const adminHtml = await getPubFile("/email-templates/admin.html");
    const custTxt = await getPubFile("/email-templates/customer.txt");
    const adminTxt = await getPubFile("/email-templates/admin.txt");

    // Format our recipient email address
    const recipEmail = `${req.body.name} <${req.body.email}>`;

    // Format our customer-bound email from received form data
    let sendHtml = template.replace("%BODY%", custHtml)
        .replace("%NAME%", req.body.name)
        .replace("%EMAIL%", req.body.email)
        .replace("%MESSAGE%", req.body.message);

    let sendTxt = custTxt
        .replace("%NAME%", req.body.name)
        .replace("%EMAIL%", req.body.email)
        .replace("%MESSAGE%", req.body.message);

    // Send our customer-bound email
    let info = await transporter.sendMail({
        from: adminEmail,
        to: recipEmail, // list of receivers
        subject: "Message Received ✔", // Subject line
        text: sendTxt, // plain text body
        html: sendHtml, // html body
    });

    if (!info.messageId) {
        res.status(200).json({ status: 0, message: "Failed to send message!" });
        return false;
    }

    sendHtml = template.replace("%BODY%", adminHtml)
        .replace("%NAME%", req.body.name)
        .replace("%EMAIL%", req.body.email)
        .replace("%MESSAGE%", req.body.message);

    sendTxt = adminTxt
        .replace("%NAME%", req.body.name)
        .replace("%EMAIL%", req.body.email)
        .replace("%MESSAGE%", req.body.message);

    info = await transporter.sendMail({
        from: recipEmail,
        to: adminEmail, // list of receivers
        subject: req.body.subject ? req.body.subject : "New Message From Website ✔", // Subject line
        text: sendTxt, // plain text body
        html: sendHtml, // html body
    });

    if (info.messageId) {
        res.status(200).json({ status: 1 });
    } else {
        res.status(200).json({ status: 0, message: "Failed to send message!" });
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 3 - Modify Our Front-End Form to Submit to Our API

Assuming you have completed the first tutorial, edit the /pages/index.js to look like this:

import React from 'react';
import Link from 'next/link';
import { Col, Container, Row, Navbar, Form, Button } from 'react-bootstrap';
import { useForm } from 'react-hook-form';
import * as yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup";
import axios from 'axios';

export default function Home() {
  // Initialize our states
  const [isSubmitting, setIsSubmitting] = React.useState(false);
  const [isSubmitted, setIsSubmitted] = React.useState(false);

  // Yup error message overrides
  const errMess = {
    req: "Please fill this out"
  };

  // Our Yup Schema for this form
  const ContactSchema = yup.object().shape({
    name: yup.string()
      .label('Full Name')
      .required(errMess.req)
      .min(3)
      .max(20),
    email: yup.string()
      .label('Email Address')
      .required(errMess.req)
      .email('Invalid Email Address'),
    message: yup.string()
      .label('Message')
      .required(errMess.req)
      .min(10)
      .max(1000),
  });

  // Destruct useForm() and set our Yup schema as the validation resolver
  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm({
    resolver: yupResolver(ContactSchema)
  });

  // Send our valid form data to our back-end API
  const submitForm = async (data) => {
    setIsSubmitting(true);

    const res = await axios({
      method: 'POST',
      url: '/api/contact-form',
      data: data
    }).then((res) => {
      setIsSubmitting(false);
      return res;
    }).catch((e) => {
      alert("An error occurred. See log for details.")
      console.error(e);
    });

    if (res.data.status === 1) {
      setIsSubmitted(true);
    } else {
      alert(res.data.message);
    }
  };

  return (
    <>
      <Navbar bg="dark" expand="lg">
        <Navbar.Brand>
          <Link href="/">
            Home
          </Link>
        </Navbar.Brand>
      </Navbar>
      <Container>
        {!isSubmitted ?
          <>
            <h1 className='mb-5'>Next.JS Form to Email Example</h1>
            <Form onSubmit={handleSubmit((data) => submitForm(data))}>
              <Row>
                <Col>
                  <Form.Group className="mb-3" controlId="nameField">
                    <Form.Label>Full Name</Form.Label>
                    <Form.Control
                      type="text"
                      placeholder="e.g. John Doe"
                      isInvalid={errors.name}
                      {...register('name')}
                    />
                    <Form.Control.Feedback type='invalid'>
                      {errors.name?.message}
                    </Form.Control.Feedback>
                  </Form.Group>
                </Col>
                <Col>
                  <Form.Group className="mb-3" controlId="emailField">
                    <Form.Label>Email Address</Form.Label>
                    <Form.Control
                      type="email"
                      placeholder="e.g. [email protected]"
                      isInvalid={errors.email}
                      {...register('email')}
                    />
                    <Form.Control.Feedback type='invalid'>
                      {errors.email?.message}
                    </Form.Control.Feedback>
                  </Form.Group>
                </Col>
                <Col lg={12}>
                  <Form.Group className="mb-3" controlId="messageField">
                    <Form.Label>Message</Form.Label>
                    <Form.Control
                      as="textarea"
                      rows={5}
                      placeholder="Please type your message..."
                      isInvalid={errors.message}
                      {...register('message')}
                    />
                    <Form.Control.Feedback type='invalid'>
                      {errors.message?.message}
                    </Form.Control.Feedback>
                  </Form.Group>
                </Col>
              </Row>
              <Row>
                <Col>
                  <Button variant="primary" type="submit" disabled={isSubmitting}>
                    {isSubmitting ? 'Sending...' : 'Submit'}
                  </Button>
                </Col>
              </Row>
            </Form>
          </>
          :
          <>
            <h1>Thank you!</h1>
            <p>Your message has been received. Please check your email for confirmation.</p>
          </>
        }

      </Container>
    </>
  )
}
Enter fullscreen mode Exit fullscreen mode

Run npm run dev and navigate to https://localhost:300 and give 'er a whirl. Hopefully, you'll see this result:

End Result 1

End Result 2


Well, it's been quite a journey! We now have a complete self-contained app that will collect data from a website user and then email it to the admin. And we require no third-party services except Gmail (which is free) and Vercel (which is also free) to host your app.

If you want the full code base for this project, you can clone my repository here.

For more great information, please visit the Designly Blog.

nodemailer Article's
30 articles in total
Favicon
Brevo smtp emails to other gmail accounts silently failing , verified domain to the rescue
Favicon
Send emails using Nodemailer (Typescript)
Favicon
Simple Emails Sending from Node.js Using Nodemailer and SMTP
Favicon
Practical Guide to Send Emails from NodeJS/Express App using Gmail and Nodemailer (Screenshots and Code)
Favicon
Sending Emails in NextJs via Nodemailer
Favicon
Sending Emails in Node.js Using Nodemailer
Favicon
Sending e-mails with Sendgrid
Favicon
NestJS Emails with react-email and nodemailer
Favicon
nodeMailer after google security update
Favicon
Beginner’s Guide On Sending Automated Emails With Node.js, Nodemailer, and Cron Jobs
Favicon
Tracking Email Activity from AWS Simple Email Service (SES)
Favicon
Envio de email com NodeJS e Gmail
Favicon
Setting up Node.js Email Server with Nodemailer and Mailtrap
Favicon
Sending Emails from a Nodejs Application using Nodemailer
Favicon
Dynamic emails with handlebars and nodemailer
Favicon
Sending e-mails with Mailtrap
Favicon
Simple Next.JS Form to Email Using React-Hook-Form and Gmail
Favicon
How to send Email with NodeJS in 2022
Favicon
How to send email attachments using nodemailer
Favicon
Send emails from your website to any user super easily!
Favicon
How to Send an Email with Nodemailer
Favicon
Send email using next.js, react-hook-form, tailwindcss & nodemailer
Favicon
Nextjs - Nodemailer - React Hook Form - Tailwindcss
Favicon
How to send mail using Nodemailer?
Favicon
I created my own email server to send emails into my gmail for My Portfolio
Favicon
How to send mail using nodemailer in Angular 11?
Favicon
How I met your...Scraper?
Favicon
Отправка писем в NestJS используя nodemailer. Публикация скриптов.
Favicon
3 ways to send emails with only few lines of code and Gmail - Javascript - Part 1
Favicon
NodeJS – Send email by Nodemailer

Featured ones: