Skip to main content

Command Palette

Search for a command to run...

How to Build a Contact Form with JavaScript and NodeMailer

Updated
How to Build a Contact Form with JavaScript and NodeMailer
V

I'm a solutions engineer lead, GitHub Star, Director of WomenDevsSG, and co founder of ragTech. I work at the intersection of tech, systems, and leadership, and this blog is where I share my journey through all three. Expect honest reflections, real experiences, and thoughts that are still forming rather than polished career advice.

Hello everyone! This article is dedicated to Oluwaseun Dabiri, as requested. I'm quite busy this week to write on a short moment's notice but I somehow managed to write and build this mini project. If you are reading this, I hope it helps. I didn't have much time to proofread so please let me know if there are any code or written typos.

This article is the alternate pure vanilla JavaScript version of my Build a Contact Form with React and NodeMailer article. Also, this version will cover more details so it's more beginner-friendly.

In this tutorial, I'll be going through step-by-step on how build a simple contact form using JavaScript and NodeMailer.

Some prerequisites that are helpful:

  • Good knowledge in HTML and JavaScript

  • Basic knowledge in back-end programming with Node and Express

Step 1: Build the HTML

Create a html page for the contact form. You can create and style it however you want. I'm using Bootstrap. For this example, here's my contact form:

Capture.PNG

Important things to note

1. The Form Element

For the HTML of the contact form, make sure the form element contains the following attributes:

  • id: can be anything you want. For my example, it's 'contact-form'

  • method = POST: because we are sending form data to the server

  • action = "send" : this is the url we are posting to for this example

  • enctype = "multipart/form-data" : this makes sure that the data will be sent in this format

2. The Input Elements

Any input elements inside the form must have a name attribute, which is needed to reference form data upon submitting. A required attribute is also recommended to make sure the user will not leave a field blank.

3. The Button Element

The button will be used to submit the form. Make sure it is inside the form element. It should have 2 attributes:

  • type="submit": specifies that it is used to submit a form

  • value="submit": sets the initial value of the button

You can find the full HTML code in my repo.

Step 2: Client-Side Functions

Now that we have a contact form, let's add some code to send the form from the client to the server. We will be using the Fetch API to POST our form data to our server.

Create a js file in the same directory as your html, mine will simply be called index.js but you name it anything you want. Remember to add it as script into your .html page.

In this script, we will do the following:

  1. Create an event listener for when the user submits the form.

  2. When the event is triggered, create a FormData object called mail based on the input values and their name attributes in the form.

  3. Call a function called sendMail() with mail passed as a parameter. This function will use Fetch API to post the mail to the url send (specified in our form element attribute).

Let's see what it looks like in code:

//get the form by its id
const form = document.getElementById("contact-form"); 

//1.
const formEvent = form.addEventListener("submit", (event) => {
  event.preventDefault();

  //2.
  let mail = new FormData(form);

  //3.
  sendMail(mail);
})

Next, let's take a look at the sendMail() function:

  1. Supply the base url with /send for the fetch().

  2. Specify the method as post since we are sending data, not getting.

  3. Specify the body as mail because we are sending this data in our request

Here's what the sendMail() function looks like:

const sendMail = (mail) => {
  //1.
  fetch("/send", {
    method: "post", //2.
    body: mail, //3.

  }).then((response) => {
    return response.json();
  });
};

Great! The client-side is done. Let's now set up our server to receive our form and use Nodemailer to send emails.

Step 3: Install Nodemailer and other dependencies

First run the following to initialize a package.json in the root directory of the project.

npm init

Then install the following dependencies:

Install the dependencies by running:

npm install express nodemailer dotenv multiparty

Step 4: Create server.js

In the root directory of your project, create a server.js file. In the first lines, we should import our dependencies:

const express = require("express");
const nodemailer = require("nodemailer");
const multiparty = require("multiparty");
require("dotenv").config();

And then initialize our app with Express:

// instantiate an express app
const app = express();

//make the contact page the the first page on the app
app.route("/").get(function (req, res) {
  res.sendFile(process.cwd() + "/public/index.html");
});

//port will be 5000 for testing
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => {
  console.log(`Listening on port ${PORT}...`);
});

Step 5: Set up Nodemailer and POST Route

Now all there's left to do is to set up the POST route to receive the submitted form data, parse it and send it via Nodemailer.

First, create a transporter object using SMTP (Simple Mail Transfer Protocol). It has the following properties:

1. host

The host depends on the email service you are using. Some common ones:

  • Gmail: smtp.gmail.com

  • Outlook: smtp-mail.outlook.com

  • Hotmail: smtp.live.com

2. port

Set to 587 by default but feel free to check out this article to learn which port number is best for your email.

3. auth

Next, we provide the credentials needed to authorize Nodemailer to use your email as the sender. Since these are private information, they will be stored in the .env file as environment variables.

Altogether, here's the transporter object for my example (I'm using a hotmail email address).

const transporter = nodemailer.createTransport({
  host: "smtp.live.com", //replace with your email provider
  port: 587,
  auth: {
    user: process.env.EMAIL,
    pass: process.env.PASS,
  },
});

Note: Gmail users need to allow third party access to let Nodemailer send emails. So turn on the 'Less Secure Apps' settings by following instructions here.

Now we have our transporter object. Next, we need to verify this connection to make the credentials are correct and Nodemailer is authorized to send emails from that address.

// verify connection configuration
transporter.verify(function (error, success) {
  if (error) {
    console.log(error);
  } else {
    console.log("Server is ready to take our messages");
  }
});

Finally, we create our POST route to do the following:

  1. Accepts the form data submitted and parse it using multiparty.

  2. After parsing it, create a mail object with from, to, subject and text properties.

  3. Use transporter.sendMail() to send the email and done.

Here's what the code looks like:

app.post("/send", (req, res) => {
  //1.
  let form = new multiparty.Form();
  let data = {};
  form.parse(req, function (err, fields) {
    console.log(fields);
    Object.keys(fields).forEach(function (property) {
      data[property] = fields[property].toString();
    });

    //2. You can configure the object however you want
    const mail = {
      from: data.name,
      to: process.env.EMAIL,
      subject: data.subject,
      text: `${data.name} <${data.email}> \n${data.message}`,
    };

    //3.
    transporter.sendMail(mail, (err, data) => {
      if (err) {
        console.log(err);
        res.status(500).send("Something went wrong.");
      } else {
        res.status(200).send("Email successfully sent to recipient!");
      }
    });
  });
});

Let's test it!

I entered and submitted the form.

test.PNG

Here's the email I received in my hotmail account, as you can see, it works!

test work.PNG

And that's Nodemailer!

Thanks for reading till the end. I hope this article has been helpful in setting your own mail sending app using Nodemailer. If it does help, please leave a like or a share. Also, feel free to leave any thoughts or questions you have about Nodemailer in the comments. Visit the repo to view the full code. Thanks and till next time, cheers!

Update: Demo site has been removed due to unwanted spams. Will return error 404 if you try to access. Apologies for the inconvenience.


See Also

M

Thanks for the blog post, Victoria. I just want to point out that the link to the article in Step 5.2 on port numbers is broken.

1
V

Updated! Thanks Marsh Mallow for kindly letting me know ;)

J

Is there a reason using a different action than "send" in the form would not work even if though I change to fetch api to fetch from the new action?

1
J

Also thank you so much for this post, it has been very helpful

1
V

Hi Jonathan Obi, you have to change both fetch and server-side side if you want to rename the action to something else. It is not mandatory that it is "send"

C

Thank you very much for sharing this!

However, I have a small wonder. The code (completely copied from your example, with the only exception being the credentials) returns 405 Method Not Allowed. The error is generated by fetch() in index.js, so server.js never runs.

The result is the same regardless of whether Live Server or Parcel server is used. However, by changing to node's server and to transporter.sendMail in the code, testing of the server.js file is succesful.

Have spent a few days on the matter, would be extremely grateful if you could point out the way (hints from articles like https :// blog.airbrake.io/blog/http-errors/405-method-not-allowed and the like do not help).

Thank you!

C

Turns out Victoria Lo's code works great. Using node, solely.

I tried the solution here, as my own didn't seem to work. When Victoria's solution didn't work either, I became suspicious. Still I can't yet satisfactory answer my own question. But at least for new/old developers who found their way to javascript bundlers point out that local "help-servers" like Live Server and Parcels own don't always work as expected. Warnings and shown errors, does not necessarily mean that the code is "wrong". The mail-area is also not the only one where middlewares does not behave as expected. The matter is apparently known, and discussed on forums like GitHub.

So a reminder to myself, before spending weeks with endless troubleshooting and youtube videos: test the solution locally with node's own server first (alternatively on a web host). If the code then does not work, and only then, do I actually know that the error lies in the own code. Unfortunately, it is difficult to avoid bundlers such as gulp, parcel or webpack, but I can't trust them to much.

Again, thank you Victoria Lo for sharing your solutions and experiences!

1
A

I seem to be getting this error every time I attempt to submit the form data.

Uncaught (in promise) SyntaxError: Unexpected token E in JSON at position 0 VM713:1

Any idea what is causing it?

W

Hi Victory, i really enjoy your work, time and energy to make great articles on your blog.

I would buying you a cup of tea but you don't have a paypal option, so that have to wait :). i have a question about the nodemailer with js. If i try the code, the only email adres that works in the browser is the EMAIL that i set in the env. file. How can i make the code work so it accepts other adresses? Have a great day. Winston

ps.

Other email adresses give the following response: '553 5.7.1 < -------@gmail.com>: Sender address rejected: not owned by user 3@7316314' in the terminal.

if any one wants to participate and help, so please!

Thanks!

V

Hi Winston H. Muijs, so the sender email can only be one but the receiver email can be multiple, comma-separated.

Please refer to the Nodemailer documentation here. Hope that helps! Cheers!

1
W

Hi Victory, i changed sender to from in my code in my mail const - and that did the job. Thanks for the help !! 🫖👍🏻Victoria Lo

I

Hey Victoria thank you for your fantastic work im right now studying front end and found your tutorial but i get this error: POST https://igorljevak.com/send 404

the site is published on that domain

i hope you find time and can help me :D

1
V

Hi Igor Ljevak, thanks for reading. Yes, I removed the demo site as there were people spamming me emails everyday. I will update the blog to let everyone know the demo site has been removed. Thanks!

I

Victoria Lo i didn't mean that :D I get that error when i follow the tutorial. its something with the part domain.com/send

my site is published on a domain and in the JS i write domain.com/send, but get the "error: POST 404"

D

Hello, first of all thanks a lot for sharing this to us!

I am pretty new using servers in js, especially using express, could you help me out managing what to display after the mail has been sent, I would like to keep my form page but only changing the button style instead of going to the blank page with "Email successfully sent to recipient!" but I can't figure out how to, please help me if you have time to

1
V

Hi Darys! Thanks for reading!

Instead of:

res.status(200).send("Email successfully sent to recipient!")

Replace with:

res.status(200).json({status: 'success'})

And it will not go to a blank page.

1
D

Thank you so much ! You actually did save me

1
B

I learnt a lot from this, thanks for sharing!

2
O

thank you so much victoria....

1
V

No problem :)

1