In this step-by-step tutorial, I’ll show you how to create a Node.js contact form, give it a personal touch, retrieve and validate data from it, and then send emails through the form via SMTP or API.
Note: you’ll need Node.js 6+ or any version released since May 2018 installed on your machine for the provided code snippets to work.
How to create a Node.js contact form
As you’re reading this article, I’m assuming you know how to install Node.js and create a new project, so allow me to keep it brief and get straight into setting up the project.
However, to freshen up your knowledge, you can check out this article on installing Node.js.
Setting up the project
First things first, let’s install some dependencies for our backend by opening the terminal and entering the following commands:
mkdir contact-form-test && cd contact-form-test– By running this command, we create a folder for our project, ensuring our project file will be within it instead of the current directory.npm init -y– This will create a package.json file that manages project dependencies and configurations.npm i express nodemailer– We need to install Express.js library and Nodemailer module as we will need them for setting up our server and sending emails with SMTP, respectively.npm i -D nodemon– This is a dev dependency that automates the process of restarting our server whenever we change our code, allowing us to see the changes we made without having us manually restart the server.
Once you install all the dependencies, your package.json file should look something like this:
{
"name": "contact-form-test", // Project name
"version": "1.0.0", // Project version
"description": "", // Description of the project
"main": "server.js", // Entry point file of the project
"scripts": {
"dev": "nodemon --watch public --watch server.js --ext js,html,css", // Script to run the server with nodemon for development
"start": "node server.js" // Script to start the server normally
},
"keywords": [], // Keywords related to the project
"author": "", // Author of the project
"license": "ISC", // License type
"dependencies": {
"express": "^4.19.2", // Express web server framework
"nodemailer": "^6.9.13" // Nodemailer for sending emails via SMTP
},
"devDependencies": {
"nodemon": "^3.1.0" // Nodemon for automatically restarting the server on code changes
}
}Bonus tips:
npm i dotenv– Although optional, this command installs the dotenv package, which loads environment variables where you can safely store your authentication credentials such as an API key. All you have to do is run the command, create an .env file in the root of your project directory, and paste your desired creds in there.- Starting from v20.6.0, Node.js has built-in support for .env files for configuring environment variables. So it is not necessary to use the dotenv package, but a lot of projects still depend on dotenv, so it’s still the de facto standard.
Configuring the server
Next, in our project folder, let’s create a new .js file called server.js which we refer to in the production script from the package.json file.
Then, simply paste the following code snippet into the server.js file:
const express = require('express');
const app = express();
const path = require('path');
const PORT = process.env.PORT || 3000;
// Middleware
app.use(express.static('public'));
app.use(express.json());
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'contactform.html'));
});
app.post('/send-email', (req, res) => {
console.log(req.body);
res.send('Data received');
});
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});Note: Later in the article, we’ll use the server.js file to add Nodemailer as a transport via SMTP and an API logic to send emails through our contact form.
Creating the contact form
Now, let’s create a new public folder called public for the static files (style.css, contactform.html, and app.js) we’re going to be using for this contact form.
In the contactform.html file, enter the following code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"> <!-- Specifies the character encoding for the HTML document -->
<link rel="stylesheet" href="/style.css"> <!-- Link to external CSS file for styling -->
<link rel="preconnect" href="https://fonts.gstatic.com"> <!-- Preconnect to load fonts faster -->
<link href="https://fonts.googleapis.com/css2?family=Poppins&display=swap" rel="stylesheet"> <!-- Google fonts link for 'Poppins' font -->
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- Responsive design meta tag -->
<title>Contact Form</title> <!-- Title of the document shown in the browser tab -->
</head>
<body>
<div class="form-container"> <!-- Container for the form to style it specifically -->
<form class="contact-form"> <!-- Form element where user inputs will be submitted -->
<h2>CONTACT</h2> <!-- Heading of the form -->
<input type="text" id="name" placeholder="Full name"><br> <!-- Input field for name -->
<input type="email" id="email" placeholder="Email"><br> <!-- Input field for email, validates email format -->
<input type="text" id="subject" placeholder="Subject"><br> <!-- Input field for subject -->
<textarea id="message" placeholder="Message" cols="30" rows="10"></textarea><br> <!-- Textarea for longer message input -->
<input type="submit" class="submit" value="Send Message"> <!-- Submit button to send the form data -->
</form>
</div>
<script src="/app.js"></script> <!-- Link to external JavaScript file for scripting -->
</body>
</html>I’ve added annotations in this code snippet as well to help you navigate through it, but feel free to delete them for a cleaner-looking code. 🙂
Styling the contact form
How about we tackle the frontend for a bit and add a personal touch to our contact form?
In the style.css file, enter the following code which will make our contact form prettier:
/* Global styles for all elements to ensure consistency */
* {
margin: 0; /* Remove default margin */
padding: 0; /* Remove default padding */
box-sizing: border-box; /* Include padding and border in the element's total width and height */
font-family: 'Poppins', sans-serif; /* Set a consistent font family throughout the app */
}
/* Styling for the html and body elements */
html, body {
background: #c0b7b7; /* Set the background color for the entire page */
}
/* Container for the form providing relative positioning context */
.form-container {
position: relative; /* Positioning context for absolute positioning inside */
left: 20%; /* Position the container 20% from the left side of the viewport */
width: 60%; /* Set the width of the container to 60% of the viewport width */
height: 100vh; /* Set the height to be 100% of the viewport height */
background-color: white; /* Set the background color of the form container */
}
/* Styling for the contact form itself */
.contact-form {
position: absolute; /* Position the form absolutely within its parent container */
top: 10%; /* Position the form 10% from the top of its container */
left: 10%; /* Position the form 10% from the left of its container */
width: 80%; /* The form width is 80% of its container */
min-height: 600px; /* Minimum height for the form */
}
/* Styling for input fields and textarea within the form */
input, textarea {
width: 100%; /* Make input and textarea elements take up 100% of their parent's width */
margin-top: 2rem; /* Add top margin to space out the elements */
border: none; /* Remove default borders */
border-bottom: 1px solid black; /* Add a bottom border for a minimalistic look */
padding: 10px; /* Add padding for better readability */
}
/* Styling for the submit button */
.submit {
border: 1px solid black; /* Add a solid border around the submit button */
padding: 1rem; /* Add padding inside the button for better clickability */
text-align: center; /* Center the text inside the button */
background-color: white; /* Set the background color of the button */
cursor: pointer; /* Change the cursor to a pointer to indicate it's clickable */
}
/* Styling for the submit button on hover */
.submit:hover {
opacity: 0.6; /* Change the opacity when hovered to give a visual feedback */
}To see how your contact form looks, you can save the file and enter the following command in your terminal:
npm run devThen, you should see the message saying that your contact form is being hosted on the custom port you defined in your .env file or the default port 3000, which allows traffic to reach it.
Finally, paste the following link in your browser’s URL bar: http://localhost:3000/ and you should see your contact form in its full glory.
How to collect data from a Node.js contact form
To collect data from our Node.js contact form, we will add functionality for handling form submissions using JavaScript, which will capture form data and send it to the server without reloading the page.
For this, we’ll use an AJAX request, which allows us to avoid a full-page reload.
I’ve made this easy for you, so all you have to do is navigate to your app.js file and enter the following code:
const contactForm = document.querySelector('.contact-form');
const name = document.getElementById('name');
const email = document.getElementById('email');
const subject = document.getElementById('subject');
const message = document.getElementById('message');
contactForm.addEventListener('submit', (e) => {
e.preventDefault(); // Prevent the default form submission
const formData = {
name: name.value,
email: email.value,
subject: subject.value,
message: message.value
};
try {
const response = await fetch('/send-email', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(formData)
});
// Wait for JSON response to be parsed
const result = await response.json();
if (!response.ok) {
// If the response is not OK, handle it by showing an alert
alert(`Failed to send message: ${result.message}`);
return; // Exit the function early if there's an error
}
// Check application-specific status from JSON when response is OK
if (result.status === 'success') {
alert('Email sent');
// Reset form fields after successful submission
name.value = '';
email.value = '';
subject.value = '';
message.value = '';
} else {
// Handle application-level failure not caught by response.ok
alert('Operation failed: ' + result.message);
}
} catch (error) {
// Handle any exceptions that occur during fetch
console.error('Error:', error);
alert('Network error or cannot connect to server');
}
});Note: I used the fetch API here which is a more modern alternative to ‘XMLHttpRequest’ and leverages async/await syntax for better error handling.
Thank you for choosing this part of the article to know about collecting data from a contact form. If you want to know how to Send email from Node.js contact form using SMTP or API, read full article on Mailtrap Blog!