Building an HTML email form – Frontend and Backend

On December 19, 2023
24min read
Veljko Ristić Content Manager @ Mailtrap

There are two ways to create an HTML email form 

  1. Use the mailto: address action element. 
  2. Use backend script to handle data and send emails.

The first method is something you shouldn’t try. Sending form data directly to an email from a web form without server-side processing isn’t recommended due to security and privacy risks. 

The following tutorial covers methods that include server-side scripting. And I added a special section to ensure that any sensitive information is encrypted and transmitted securely. 

First, I cover the front end that sends an HTTP POST request to the backend. Next, I go into the backend to send emails, using Laravel, Java, Node.js, Python, or C#.  Then, I’ll give you tips and some examples of security measures. 

How to create an HTML contact form – Quick guide 

As said, I’ll be covering the backend scripting for a few popular languages and frameworks. So, to make the HTML code easily integrated with the scripts, it’s critical to ensure the HTML form is universally compatible with each language. 

Here’s how to create a simple HTML form: 

  1. Form structure

Start with a basic HTML form structure. The action attribute should be set to a generic handler, which can be specified in the backend script.

<form action="process-form" method="post" enctype="multipart/form-data">
  <!-- Form elements will go here -->
</form>

Note: The enctype="multipart/form-data" is important if you plan to include file uploads. It’s compatible with all backend technologies. The backend scripts also feature exemplary snippets without the support for file uploads. 

  1. Input elements 

As needed, you can include the following elements.  

Text fields

<label for="name">Name:</label>
<input type="text" id="name" name="name">

Message field

<label for="message">Message:</label>
<textarea id="message" name="message" rows="4" cols="50">
</textarea>

Message field notes:

  • <label for="message">: This label is associated with the textarea. The for attribute should match the id of the textarea, which helps with accessibility.
  • <textarea>: This is the element used for multi-line text input.
  • id="message": The ID of the textarea, which is referenced by the label.
  • name="message": The textarea name attribute is important as it’s the key that will be used to access this piece of data on the server side.
  • rows="4" and cols="50": These attributes define the size of the textarea. rows specifies the number of lines, and cols specifies the width in terms of character columns. You can adjust these numbers based on your layout requirements.

Email field

<label for="email">Email:</label>
<input type="email" id="email" name="email">

Submit button

<input type="submit" value="Submit">
  1. Form action

The action attribute in the form should be a placeholder that will be replaced by the specific endpoint of your backend technology (Laravel, Java, Node.js, Python, or C#).

For instance, in Laravel, you might set it to a route defined in your web routes, while in Node.js, it might point to an Express.js route.

  1. Backend integration (quick notes)
  • Laravel: Use a route in web.php to handle the form submission.
  • Java: Set up a servlet or a Spring Controller to process the form.
  • Node.js: Use an Express.js route to handle the POST request.
  • Python (Flask/Django): Define a view function or a Django view to process the form.
  • C# (ASP.NET): Use a controller action in an ASP.NET MVC application.
  1. Processing form data

Each backend technology will have its way of accessing form data (e.g., request.getParameter() in Java, req.body in Node.js, request.form in Flask).

Ensure that the form data is properly validated and sanitized in the backend script to prevent security vulnerabilities.

  1. Sending emails

Each backend will have a different method for sending emails. Use the respective libraries or frameworks available for each technology.

Pro tips:

  • You can also embed radio buttons or checkboxes in the form. Here I aimed for simplicity, focusing on the email and message, so there aren’t checkboxes and radio buttons. 
  • Consider implementing CAPTCHA to prevent spam submissions and confirm that the form is being filled out by a human. Later, I’ll show you how to integrate Google’s reCAPTCHA.
  • It’s best to use a proper email-sending service like Email Sending by Mailtrap Email Delivery Platform to ensure your emails securely land at the desired address. 

HTML email form with CSS styles

Without CSS, the form looks bland and might actually affect the conversions. So, I’ll make it more elegant, and the exemplary form below contains Name, Email, and Message fields, plus the Submit button. 

The HTML form

<!DOCTYPE html>
<html>
<head>
    <title>Contact Form</title>
    <link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
    <div class="container">
        <form id="contact-form" action="process-form.php" method="post">
            <div class="form-group">
                <label for="name">Name:</label>
                <input type="text" id="name" name="name" required>
            </div>

            <div class="form-group">
                <label for="email">Email:</label>
                <input type="email" id="email" name="email" required>
            </div>

            <div class="form-group">
                <label for="message">Message:</label>
                <textarea id="message" name="message" rows="4" required></textarea>
            </div>

            <div class="form-group">
                <input type="submit" value="Submit">
            </div>
        </form>
    </div>
</body>
</html>

Quick Explainer:

  • HTML Structure: The form is wrapped in a div with a class container for styling purposes. Each input field is enclosed in a div with a class form-group for better control over spacing and layout.
  • Required Fields: The required attribute in the input fields ensures that the form cannot be submitted without filling these fields.

The CSS (style.css)

body {
    font-family: Arial, sans-serif;
    background-color: #f4f4f4;
    margin: 0;
    padding: 0;
}

.container {
    width: 70%;
    margin: 30px auto;
    padding: 20px;
    background: #fff;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}

.form-group {
    margin-bottom: 15px;
}

.form-group label {
    display: block;
    margin-bottom: 5px;
}

.form-group input[type="text"],
.form-group input[type="email"],
.form-group textarea {
    width: 100%;
    padding: 8px;
    border: 1px solid #ddd;
    border-radius: 4px;
    box-sizing: border-box;
}

.form-group input[type="submit"] {
    background-color: #5cb85c;
    color: white;
    padding: 10px 15px;
    border: none;
    border-radius: 4px;
    cursor: pointer;
}

.form-group input[type="submit"]:hover {
    background-color: #4cae4c;
}

Quick Explainer:

  • The body style sets the font and background color of the page.
  • The container class styles the form’s container with a white background, centered alignment, and a subtle box shadow for a modern look.
  • The form-group class provides spacing between each form element.
  • Input fields (text, email, textarea) are styled to take full width with some padding and border-radius for a pleasant rounded corner look.
  • I changed the submit button type a bit. It’s styled with a green background color, which changes to a slightly darker green on hover for a subtle interactive effect.

Reminder: Replace process-form.php (placeholder) in the form’s action attribute with the actual script that will process the form data.

JavaScript to enhance the form behavior

HTML provides only basic validation attributes. Whereas, JavaScript allows for more dynamic and responsive validation that can improve user experience. But note that this isn’t by any means a security measure. 

However, it’s a much better approach to handling the form code and it can markedly improve user experience. For example, they won’t need to wait for the web page to refresh to see if the submission was successful. 

Now, to create a JavaScript-enhanced HTML email form that works seamlessly with various backend technologies (like Laravel, Java, Node.js, Python, and C#), I’ll focus on two main aspects: 

  1. Form validation 
  2. AJAX submission

Check the following code to see how it’s done. 

Form validation 

document.getElementById('contact-form').addEventListener('submit', function(event) {
    var nameInput = document.getElementById('name').value.trim();
    var emailInput = document.getElementById('email').value.trim();
    var messageInput = document.getElementById('message').value.trim();
    var emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; // Simple email regex pattern

    if (nameInput === '') {
        alert('Please enter your name.');
        event.preventDefault();
    } else if (!emailPattern.test(emailInput)) {
        alert('Please enter a valid email address.');
        event.preventDefault();
    } else if (messageInput === '') {
        alert('Please enter your message.');
        event.preventDefault();
    }
});

Pro Tip:

The script above relies on a simple regex to validate an email, and it works for exemplary purposes. But on production, assuming you’re getting a lot of submissions, it may return false positives or false negatives, unless the regex is a mile long. 

To offset that, consider using a proper validation library such as:

Submitting form data with AJAX

I made script below universal so that it dynamically adapts to the server-side technologies. 

document.getElementById('contact-form').addEventListener('submit', function(event) {
    event.preventDefault();

    var formData = new FormData(this);
    var actionUrl = this.getAttribute('action'); // Dynamically get the action URL

    var xhr = new XMLHttpRequest();
    xhr.open('POST', actionUrl, true);
    xhr.onload = function () {
        if (xhr.status === 200) {
            alert('Email sent successfully!');
        } else {
            alert('An error occurred while sending the email.');
        }
    };
    xhr.send(formData);
});

Quick Notes:

  • Dynamic Action URL: Instead of hardcoding the action URL (like send-email.php), the script now dynamically retrieves the URL from the form’s action attribute. This means you can set the action URL to any server-side script (PHP, Java, Node.js, etc.), and the JavaScript will adapt accordingly.
  • Form Data: The FormData object is used to capture and send all the form data, making it easy to handle on the server side, regardless of the technology used.
  • Ensure that the endpoint (e.g., route in Laravel, or controller in ASP.NET) specified in the form’s action attribute can handle FormData sent via POST request.
  • Each backend technology will have its way of extracting and processing the data, but the front-end submission process remains consistent and universal.

Backed for HTML5 forms to send emails

I’ll give you backend script examples in five popular scripting languages. Note that the exemplary scripts are geared toward Mailtrap users. 

HTML form email in PHP (Laravel)

The framework’s Mail class, combined with its view-based approach to email content, allows for easy and flexible email handling.

Before you proceed with the backend, you need to make some changes to frontend HTML file.
First create a view file, for example /resources/views/welcome.blade.php. Then, ensure the specified route is matching this ‘welcome’ view.

Note: Importing CSS and JS files parts are a little bit different from the original file. By default, these assets should be in the ‘public’ folder of your Laravel project.

<link href="{{ asset('css/app.css') }}" rel="stylesheet" type="text/css" >
<script src="{{ asset('js/app.js') }}" ></script>
  1. Set up Laravel for email sending

Before you can send emails with Laravel, you need to configure the mail settings in your .env file. Laravel supports various mail drivers and here’s an example of setting up SMTP in the .env file:

MAIL_MAILER=smtp
MAIL_HOST=your_mailtrap_smtp_host.io
MAIL_PORT=2525
MAIL_USERNAME=your_mailtrap_username
MAIL_PASSWORD=your_mailtrap_password
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=example@example.com
MAIL_FROM_NAME="${APP_NAME}"

Replace the values with your Mailtrap mail server details. And if port 2525 doesn’t work for you, use 587. 

  1. Create a mailable class

Laravel uses “mailable” classes to encapsulate each email’s data and configuration. You can generate a new mailable class using the Artisan command:

php artisan make:mail ContactFormMail

This command creates a new class in the App\Mail namespace. You can define the build method to configure the email’s subject, view, and data:

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Mail\Mailables\Attachment;
use Illuminate\Queue\SerializesModels;


class ContactFormMail extends Mailable
{
    use Queueable, SerializesModels;

    public $formData;

    public function __construct($data)
    {
        $this->formData = $data;
    }

    /**
     * Get the message content definition.
     */
    public function content(): Content
    {
        return new Content(
            view: 'emails.contact',
        );
    }

}
  1. Create the email view

Create a view (e.g., resources/views/emails/contact.blade.php) that Laravel will use for the email content. You can use Blade templating to dynamically populate data from the form:

<!DOCTYPE html>
<html>
<head>
    <title>New Contact Form Submission</title>
</head>
<body>
    <h1>Contact Form Submission</h1>
    <p>Name: {{ $formData['name'] }}</p>
    <p>Email: {{ $formData['email'] }}</p>
    <p>Message: {{ $formData['message'] }}</p>
</body>
</html>
  1. Handle form submission in a controller

Important Note:

The frontend may prevent the default event when submitting the email form and just send an API call to the backend server. In this case, the API URL is /api/email. and the API request is handled in routes/api.php. To fix this, add the following line in the apip.php file. I omitted the ‘/api’ because of the structure of the project.

Route::post('/email', [EmailController::class, 'sendEmail']);

In the controller that handles the form submission, use the Mail facade to send the email:

use Illuminate\Support\Facades\Mail;
use App\Mail\ContactFormMail;

public function sendEmail(Request $request)
{
    $formData = $request->all();

    Mail::to('receiver@example.com')->send(new ContactFormMail($formData));

    // Return response or redirect
}
  1. Security and validation

Ensure to validate and sanitize the form data to prevent security issues like XSS and injection attacks. Laravel provides a robust validation system that you can use in your controller:

$request->validate([
    'name' => 'required',
    'email' => 'required|email',
    'message' => 'required'
]);

For a more detailed guide on PHP email contact form, check our blog post in the link. 

Handling HTML form with attachments in Laravel 

  1. Modify the HTML form

Ensure your HTML form includes a file input and is set to send data as multipart/form-data:

<form action="/submit-form" method="post" enctype="multipart/form-data">
    <!-- Other form fields -->
    <label for="attachment">File:</label>
    <input type="file" id="attachment" name="attachment" accept="image/png" required>    

</form>
  1. Update the mailable class

In your mailable class (ContactFormMail), you’ll need to modify the build method to handle the attachment:

/**
     * Get the attachments for the message.
     *
     * @return array<int, \Illuminate\Mail\Mailables\Attachment>
     */
    public function attachments(): array
    {
        return [
            Attachment::fromPath($this->formData['attachment'])
                ->as('image.png')
                ->withMime('image/png'),
        ];
    }

The code checks if an attachment is included in the form data and attaches it to the email. It also creates an email attachment in Laravel by using the Attachment::fromPath() method which specifies the file to attach via a path provided in $this->formData['attachment'].

The ('image.png') method sets the name of the file as it will appear in the email to 'image.png'. Lastly, the withMime('image/png') method defines the MIME type of the attachment to indicate that the attached file is a PNG image.

  1. Handle the file upload in the controller

In the controller that processes the form submission, you’ll need to handle the file upload and pass the file to the mailable:

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Mail\ContactFormMail;
use Illuminate\Support\Facades\Mail;


public function sendEmail(Request $request) {

        $request->validate([
            'name' => 'required',
            'email' => 'required | email',
            'message' => 'required',
            'attachment' => 'file|max:2048'
        ]);

        $formData = $request->all();

        // Handle file upload
        if ($request->hasFile('attachment') && $request->file('attachment')->isValid()) {
            $formData['attachment'] = $request->file('attachment');
        }
        
        Mail::to('receiver@gmail.com')->send(new ContactFormMail($formData));
    }

The sendEmail method begins by validating the incoming HTTP request, ensuring that the 'name', 'email', and 'message' fields are provided. Also, it ensures that if an 'attachment' is included, the file doesn’t exceed 2048 kilobytes.

When validation passes, all the data from the request, including both the form fields and files, are retrieved and stored in the $formData variable using the all() method.

The function checks if an attachment was uploaded and is valid by using the hasFile() and isValid() methods, and then it updates the $formData array with the attachment file object.

If the attachment is present and valid, its value in the $formData array will be an UploadedFile instance representing the file; otherwise, it will remain as originally provided or might not exist in the array.

The next line of code sends an email to 'receiver@gmail.com' using Laravel’s built-in Mail facade, providing a new instance of the ContactFormMail mailable class initialized with the $formData.

HTML form email in Java

In this section, I’ll focus on using JavaMail API, a popular and versatile library for email handling in Java applications.

  1. Add JavaMail dependency

The dependency needs to be added to your project. And if you’re using Maven, add the following dependency to the pom.xml:

dependency below to the 'pom.xml': 
<dependency>
            <groupId>com.sun.mail</groupId>
            <artifactId>javax.mail</artifactId>
            <version>1.6.2</version>
        </dependency>
  1. Configure the mail session

Set up the mail session properties such as the SMTP server details (use your Mailtrap credentials to replace the placeholders):

Properties props = new Properties();
props.put("mail.smtp.host", "your_mailtrap_smtp_host.io"); // SMTP Host
props.put("mail.smtp.port", "587"); // TLS Port
props.put("mail.smtp.auth", "true"); // Enable Authentication
props.put("mail.smtp.starttls.enable", "true"); // Enable STARTTLS

// Create session with authenticator
Session session = Session.getInstance(props, new javax.mail.Authenticator() {
    protected PasswordAuthentication getPasswordAuthentication() {
        return new PasswordAuthentication("username", "password");
    }
});
  1. Use the MimeMessage class to create and send the email
try {
    MimeMessage message = new MimeMessage(session);
    message.setFrom(new InternetAddress("from@example.com"));
    message.addRecipient(Message.RecipientType.TO, new InternetAddress("to@example.com"));
    message.setSubject("Subject Line");

    // Set the email body
    String emailContent = "Email body content"; // This can be HTML or plain text
    message.setContent(emailContent, "text/html");

    // Send the email
    Transport.send(message);
    System.out.println("Email sent successfully.");
} catch (MessagingException e) {
    e.printStackTrace();
}
  1. Form data handling 

To handle form data, you have to build both front-end and back-end. There are several Java frameworks that can be used to build MVC structure. Here, I’ll use Spring framework to receive form data from the frontend and handle the data to constructed and sent.

To achieve that, there are two intermediary steps:

  1. Move the HTML file into /src/main/resources/templates and the other CSS and JS files into /src/main/resources/static.
  2. Add configuration/ tomcat server/ and select version of current installed tomcat server.

Assuming you have a web application that receives form submissions, you’d extract the form data and use it to construct your email content. For example:

@SpringBootApplication
@Controller
public class DemoApplication {
	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}
    
	@PostMapping("/email")
	public String sayHello(@RequestParam("name") String name,
						   @RequestParam("email") String email,
						   @RequestParam("message") String message){
    try {
			System.out.println(name);
			System.out.println(email);
			System.out.println(message);
			sendEmail(name, email, message, file);
			return "index";
		} catch (MessagingException | IOException e) {
    HandleException(e);
		}

}

Handling HTML form with attachments in Java

  1. Modify the HTML form

Ensure your HTML form includes a file input and is set to send data as multipart/form-data:

<form action="/submit-form" method="post" enctype="multipart/form-data">
    <!-- Other form fields -->
    <input type="file" name="attachment">
    <input type="submit" value="Submit">
</form>
  1. Process the attachments

In your Java backend (e.g., a servlet), you’ll need to handle the file upload.

  1. Attach the file to the email

Modify the email sending logic to include the attachment. You’ll use a MimeMultipart object to combine text and attachment parts:

try {
    MimeMessage message = new MimeMessage(session);
    message.setFrom(new InternetAddress("from@example.com"));
    message.addRecipient(Message.RecipientType.TO, new InternetAddress("to@example.com"));
    message.setSubject("Subject Line");

    // Create a multipart message
    Multipart multipart = new MimeMultipart();

    // Create the message part
    BodyPart messageBodyPart = new MimeBodyPart();
    String emailContent = "Email body content"; // This can be HTML or plain text
    messageBodyPart.setContent(emailContent, "text/html");
    multipart.addBodyPart(messageBodyPart);

    // Add the file attachment
    if (fileContent != null) {
        messageBodyPart = new MimeBodyPart();
        DataSource source = new ByteArrayDataSource(fileContent, "application/octet-stream");
        messageBodyPart.setDataHandler(new DataHandler(source));
        messageBodyPart.setFileName(fileName);
        multipart.addBodyPart(messageBodyPart);
    }

    // Send the complete message
    message.setContent(multipart);
    Transport.send(message);
    System.out.println("Email sent successfully with attachment.");
} catch (MessagingException e) {
    e.printStackTrace();
}

Important notes:

  1. The provided method sendEmailWithAttachments is designed to send an email with an attachment using JavaMail API, setting up SMTP settings for connecting to Mailtrap’s sandbox server for the outgoing email.
  2. It configures SMTP server properties such as the host, port, authentication, and STARTTLS setting, then establishes a mail session with authentication details for the sender.
  3. A MimeMessage object is created and populated with the sender and recipient’s email addresses, along with setting the subject line for the email.
  4. The message body is constructed using a MimeBodyPart, which includes a text message that contains the sender’s personal details (name, email, message), and this part is added to the Multipart container.
  5. Another MimeBodyPart is dedicated to handling the file attachment, retrieving the input stream of the MultipartFile, and setting the content type. Then, it’s appended to the multipart which accidentally contains two references to messageBodyPart instead of one for messageBodyPart and one for attachmentPart.
  6. Finally, the entire email content including the text and attachment is sent using the Transport.send() method, and a confirmation message is printed to indicate successful sending. However, there’s a bug in adding both parts to the Multipart which should be corrected.

HTML form email in Node.js

Here, I’ll use Nodemailer, which is a Node.js module for email notifications. And you should easily set it up to send emails. 

  1. Install Nodemailer

Install Nodemailer in your Node.js project by running the command below in the project directory:

npm install nodemailer
  1. Import and configure Nodemailer

In your Node.js script, where you handle the form submission, import Nodemailer, and set up the transporter. This transporter will be responsible for sending your emails.

const nodemailer = require('nodemailer');

let transporter = nodemailer.createTransport({
    host: 'your_mailtrap_host.io',
    port: 587,
    secure: false, // true for 465, false for other ports
    auth: {
        user: 'your_mailtrap_username',
        pass: 'your_mailtrap_password'
    }
});

Replace the host, user, and pass with your Mailtrap SMTP credentials.

  1. Set up email options

Define the email options including the sender, receiver, subject, and body of the email.

let mailOptions = {
    from: '"Sender Name" <sender@example.com>',
    to: 'receiver@example.com',
    subject: 'New Form Submission',
    html: '<p>This is the form submission content</p>' // HTML body
};

The html field can be dynamically generated based on the form data received.

  1. Send the email

Use the transporter to send the email with the defined options.

transporter.sendMail(mailOptions, (error, info) => {
    if (error) {
        return console.log(error);
    }
    console.log('Message sent: %s', info.messageId);
});
  1. Handle the form data

In a real-world scenario, you’ll receive form data through an HTTP request. If you’re using Express.js, you can set up a route to handle POST requests from your form. Here’s an example containing a few intermediary steps:

  1. Create a new Express project
npx express-generator

For earlier Node versions, install the application generator as a global npm package and then launch it:

npm install -g express-generator
express

For example, the following code snippet creates an Express app named myapp. The app will be created in a folder named myapp in the current working directory and the view engine will be set to Pug:

express --view=pug myapp

2. Install dependencies

cd myapp
npm install

3. Place the HTML, CSS, and JS files in the ‘public’ folder

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index');
});

The snippet sets up a route handler with Express’s router. The GET method is used because this route will respond to HTTP GET requests. The first argument '/' is the path or endpoint, which in this case is the root of the site. This means that this route will match the home page (e.g., http://www.example.com/).

The Route Handler Function (function(req, res, next)) is the second argument. It represents a callback function that gets called when an HTTP GET request is made to the specified path ('/'). This function takes three parameters:

  • req: Short for “request,” it represents the HTTP request and has properties for the request query string, parameters, body, HTTP headers, etc.
  • res: Short for “response,” it represents the HTTP response that an Express app sends when it receives an HTTP request.
  • next: A function in the Express router which, when invoked, executes the middleware succeeding the current middleware.

Inside the callback, res.render is called to render a view template. res.render looks up the template engine ( Pug, EJS, Handlebars, etc.) configured in your Express app, and then render the template named 'index'.

After the template is rendered, the resulting HTML will be sent as the response to the client. The 'index' refers to index.*, where * is the file extension corresponding to the template engine used (e.g., index.pug, index.ejs, index.hbs, etc.).

To summarize, when a user visits the root URL of the site, the server will respond by rendering and returning the 'index' template, displaying the home page to the user.

const express = require('express');
const app = express();

app.use(express.urlencoded({ extended: true })); // for parsing application/x-www-form-urlencoded

app.post('/submit-form', (req, res) => {
    const { name, email, message } = req.body;
    // Construct the email content
    let emailContent = `<p>Name: ${name}</p><p>Email: ${email}</p><p>Message: ${message}</p>`;

    // Set up and send the email using Nodemailer
    // ...

    res.send('Form submitted successfully!');
});

app.listen(3000, () => {
    console.log('Server started on port 3000');
});

Handling HTML for with attachments in Node.js

  1. Modify the HTML form

Ensure your HTML form includes a file input and is set to send data as multipart/form-data:

<form action="/submit-form" method="post" enctype="multipart/form-data">
    <!-- Other form fields -->
    <input type="file" name="attachment">
    <input type="submit" value="Submit">
</form>
  1. Process the attachment in Node.js

Install multer which is a Node.js middleware for handling multipart/form-data and it’s used for uploading files. You can install it using npm:

npm install multer

Proceed to set up multer in your route. If using Express.js route, here’s how to configure multer to handle files. 

const multer = require('multer');
const upload = multer({ dest: 'uploads/' });

app.post('/submit-form', upload.single('attachment'), (req, res) => {
    // req.file contains information about the uploaded file
    // req.body contains the text fields
});
  1. Attach the file to the email

Check how to modify the Nodemailer setup to include the file attachment:

router.post('/email', upload.single('attachment'), (req, res) => {

    let transporter = nodemailer.createTransport({
        host: 'your_mailtrap_host.io',
        port: 2525,
        secure: false, 
        auth: {
        user: 'username',
        pass: 'password'
        }
    });

    const { name, email, message } = req.body;
    // Construct the email content
    let emailContent = `<p>Name: ${name}</p><p>Email: ${email}</p><p>Message: ${message}</p>`;

    let mailOptions = {
        from: '"Sender Name" <sender@example.com>',
        to: 'receiver@example.com',
        subject: 'New Form Submission',
        html: `<p>This is the form submission content</p><br>${emailContent}`,
        attachments: [
            {
                filename: req.file.originalname,
                path: req.file.path
            }
        ]
    };

    transporter.sendMail(mailOptions, (error, info) => {
        if (error) {
            return console.log(error);
        }
        console.log('Message sent: %s', info.messageId);
        // Delete the file after sending to avoid cluttering the server
        fs.unlink(req.file.path, (err) => {
            if (err) throw err;
        });
    });

    res.send('Form submitted successfully!');
});

HTML form email in Python

Python offers several libraries for sending emails, with smtplib and email being the most commonly used for basic email functionalities. I’ll use these libraries to show you how to send emails from an HTML form.

  1. Import libraries
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
  1. Configure Mailtrap SMTP
smtp_server = 'your_mailtrap_smtp.host.io'
smtp_port = 587
smtp_user = 'your_mailtrap_username'
smtp_password = 'your_mailtrap_password'

Don’t forget to change the placeholder credentials with your Mailtrap credentials. 

  1. Create a MIME message

Use MIMEMultipart to create a MIME message which can handle different parts (like text, HTML, and attachments).

msg = MIMEMultipart()
msg['From'] = smtp_user
msg['To'] = 'receiver@example.com'
msg['Subject'] = 'New Form Submission'

# Email body
body = 'This is the form submission content'
msg.attach(MIMEText(body, 'html'))
  1. Use smtplib to send the email
with smtplib.SMTP(smtp_server, smtp_port) as server:
    server.starttls()
    server.login(smtp_user, smtp_password)
    server.send_message(msg)
    print("Email sent successfully!")
  1. Handle form data

You’ll receive form data through an HTTP request. If you’re using a web framework like Flask or Django, you can set up a route to handle POST requests from your form tag:

# Example using Flask
from flask import Flask, request
app = Flask(__name__)

@app.route('/submit-form', methods=['POST'])
def submit_form():
    name = request.form.get('name')
    email = request.form.get('email')
    message = request.form.get('message')

    # Construct the email content
    body = f"<p>Name: {name}</p><p>Email: {email}</p><p>Message: {message}</p>"

    # Send email logic here

    return 'Form submitted successfully!'

if __name__ == '__main__':
    app.run(debug=True)

Note: If you need a more detailed guide on using Django instead of Flask, check our article in the link. 

Handle HTML form with attachments in Python

  1. Modify the HTML form

Ensure your HTML form includes a file input and is set to send data as multipart/form-data:

<form action="/email" method="post" enctype="multipart/form-data">
    <!-- Other form fields -->
    <input type="file" name="attachment">
    <input type="submit" value="Submit">
</form>
  1. Process the attachment

I’ll continue with Flask as the web framework, and here’s how to handle file uploads. 

from flask import Flask, request
from werkzeug.utils import secure_filename

app = Flask(__name__)

@app.route('/submit-form', methods=['POST'])
def send_email(body, path, filename):
    name = request.form.get('name')
    email = request.form.get('email')
    message = request.form.get('message')

    # Check if the post request has the file part
    if 'attachment' not in request.files:
        return 'No file part'
    file = request.files['attachment']
    if file.filename == '':
        return 'No selected file'
    if file:
        filename = secure_filename(file.filename)
        file.save(os.path.join(path, filename))
        # Now you can attach this file to your email
  1. Attach the file to the email

Modify the email sending logic to include the file attachment. You’ll use MIMEBase from the email module for this:

from email.mime.base import MIMEBase
from email import encoders

# ... [previous setup code]

# Attach the file
path = ‘path/to/save/’
with open('path' + filename, 'rb') as attachment:
    part = MIMEBase('application', 'octet-stream')
    part.set_payload(attachment.read())
    encoders.encode_base64(part)
    part.add_header(
        'Content-Disposition',
        f'attachment; filename= {filename}',
    )
    msg.attach(part)

# ... [email sending logic]

Below is the full code for server.py; run this application using the 'python server.py' command.

from flask import Flask, render_template, request
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from werkzeug.utils import secure_filename
import os
from email.mime.base import MIMEBase
from email import encoders


app = Flask(__name__)
def send_email(body, filename):
    smtp_server = 'sandbox.smtp.mailtrap.io'
    smtp_port = 587
    smtp_user = 'username'
    smtp_password = 'password'


    msg = MIMEMultipart()
    msg['From'] = smtp_user
    msg['To'] = 'receiver@example.com'
    msg['Subject'] = 'New Form Submission'

    with open('C:/email/' + filename, 'rb') as attachment:
        part = MIMEBase('application', 'octet-stream')
        part.set_payload(attachment.read())
        encoders.encode_base64(part)
        part.add_header(
            'Content-Disposition',
            f'attachment; filename= {filename}',
        )
        msg.attach(part)

    # Email body
    msg.attach(MIMEText(body, 'html'))

    with smtplib.SMTP(smtp_server, smtp_port) as server:
        server.starttls()
        server.login(smtp_user, smtp_password)
        server.send_message(msg)
        print("Email sent successfully!")

@app.route('/')
def home():
    return render_template('index.html')

@app.route('/email', methods=['POST'])
def post_data():
    
    name = request.form.get('name')
    email = request.form.get('email')
    message = request.form.get('message')

    # Check if the post request has the file part
    if 'attachment' not in request.files:
        return 'No file part'
    file = request.files['attachment']
    if file.filename == '':
        return 'No selected file'
    if file:
        filename = secure_filename(file.filename)
        file.save(os.path.join('C:/email/', filename))

    # Construct the email content
    body = f"<p>Name: {name}</p><p>Email: {email}</p><p>Message: {message}</p>"
    send_email(body, filename)
    # Send email logic here
    return 'Form submitted successfully!'
if __name__ == '__main__':
    app.run(debug=True)

The project structure is as follows.

Your-app —server,py
|–templates — index.html
|–static – style.css
| – script.js

HTML form email in C#

In C#, sending emails can be accomplished using the System.Net.Mail namespace, which provides a set of classes for sending emails to an SMTP server for delivery. 

  1. Include the namespace
using System.Net.Mail;
using System.Net;
  1. Configure SMTP
SmtpClient client = new SmtpClient("your_mailtrap_smtp_host.io", 587)
{
    Credentials = new NetworkCredential("your_mailtrap_username", "your_mailtrap_password"),
    EnableSsl = true
};

Replace the SMTP host, port, username, and password with your Mailtrap credentials.

  1. Create the MailMessage object

The object represents the email that’s going to be sent. 

MailMessage mailMessage = new MailMessage
{
    From = new MailAddress("your_email@example.com"),
    Subject = "New Form Submission",
    Body = "<p>This is the form submission content</p>",
    IsBodyHtml = true
};
mailMessage.To.Add("receiver@example.com");

The IsBodyHtml property is set to true to indicate that the body of the email is HTML.

  1. Use the SmtpClient to send the MailMessage
try
{
    client.Send(mailMessage);
    Console.WriteLine("Email sent successfully.");
}
catch (Exception ex)
{
    Console.WriteLine("Exception caught in sending email: {0}", ex.ToString());
}
  1. Handle form data

You’ll receive form data through an HTTP request. If you’re using ASP.NET, you can set up a controller action to handle POST requests from your form. Check the example below. 

[HttpPost]
public ActionResult SubmitForm(string name, string email, string message)
{
    // Construct the email content
    string emailContent = $"<p>Name: {name}</p><p>Email: {email}</p><p>Message: {message}</p>";

    // Send email logic here

    return View();
}

Handle HTML form with attachments in C#

  1. Modify the HTML form

Ensure your HTML form includes a file input and is set to send data as multipart/form-data:

<form action="/YourController/SubmitForm" method="post" enctype="multipart/form-data">
    <!-- Other form fields -->
    <input type="file" name="attachment">
    <input type="submit" value="Submit">
</form>
  1. Handle file uploads in ASP.NET
using System.Web;
using System.IO;

[HttpPost]
public ActionResult SubmitForm(HttpPostedFileBase attachment)
{
    if (attachment != null && attachment.ContentLength > 0)
    {
        var fileName = Path.GetFileName(attachment.FileName);
        var path = Path.Combine(Server.MapPath("~/App_Data/uploads"), fileName);
        attachment.SaveAs(path);
        // Now you can attach this file to your email
    }

    // Other form processing logic here

    return View();
}
  1. Modify the sending logic to attach the file to the email
// ... [previous setup code]

if (attachment != null && attachment.ContentLength > 0)
{
    var fileName = Path.GetFileName(attachment.FileName);
    var path = Path.Combine(Server.MapPath("~/App_Data/uploads"), fileName);
    attachment.SaveAs(path);

    // Attach the file
    mailMessage.Attachments.Add(new Attachment(path));
}

// ... [email sending logic]

// Clean up: Dispose of the message and delete the file after sending
mailMessage.Dispose();

Further reading:

Security units for HTML form

Since we’re dealing with front and backend, security measures can get pretty complicated pretty fast. So, I’ll break things down for you and provide exemplary code snippets wherever possible. 

However, keep in mind that secure data-handling, both static and in-transit, isn’t a set-and-forget deal. You’ll need to get back to the code, review it from time to time, and tighten security protocols if necessary. 

Front-End HTML form security

  1. Client-side validation: While JavaScript validation enhances user experience, remember it’s not a security measure. All validations must be rechecked on the server side.
  1. HTTPS: Ensure that your form is served over HTTPS to encrypt data transmitted between the client and server.
  1. CSRF tokens: For forms that perform actions on the server (like database updates), include Cross-Site Request Forgery (CSRF) tokens to prevent unauthorized submissions.
// In a Laravel Blade template
<form method="POST" action="/submit-form">
    @csrf
    <!-- form elements -->
</form>
  1. Captcha: Implement CAPTCHA to prevent automated submissions by bots. Here’s an example with Google’s reCAPTCHA. 
<script src='https://www.google.com/recaptcha/api.js'></script>
<form action="form_submit.php" method="post">
    <!-- other form elements -->
    <div class="g-recaptcha" data-sitekey="your_site_key"></div>
    <input type="submit" value="Submit">
</form>

Backend security 

Laravel (PHP)

  1. Validation
$validated = $request->validate([
    'email' => 'required|email',
    'name' => 'required|max:255',
    // other fields
]);
  1. Escaping output
{{ htmlspecialchars($string, ENT_QUOTES, 'UTF-8') }}
  1. CSRF protection: Laravel automatically generates and verifies CSRF tokens for each active user session.
  1. Email injection prevention: Use Laravel’s mailing classes which automatically handle email header injection attempts.

Java

  1. Input Validation (Using Apache Commons Validator)
EmailValidator emailValidator = EmailValidator.getInstance();
if (!emailValidator.isValid(email)) {
    // Handle invalid email
}
  1. Prepared statements (using JDBC)
String query = "INSERT INTO users (name, email) VALUES (?, ?)";
PreparedStatement preparedStatement = connection.prepareStatement(query);
preparedStatement.setString(1, name);
preparedStatement.setString(2, email);
preparedStatement.executeUpdate();
  1. CSRF tokens: Implement CSRF tokens in forms.
  2. Sanitize data: Ensure all data is sanitized before processing or storing.

Node.js

  1. Express validator
const { body, validationResult } = require('express-validator');
app.post('/submit-form', [
    body('email').isEmail(),
    body('name').not().isEmpty()
], (req, res) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
        return res.status(400).json({ errors: errors.array() });
    }
    // Process form data
});
  1. Helmet
const helmet = require('helmet');
app.use(helmet());
  1. CSRF protection: Use modules like csurf for CSRF protection.
  2. Sanitize input: Use libraries like DOMPurify to sanitize user input.

Python (Django/Flask)

  1. Django forms
from django import forms

class ContactForm(forms.Form):
    name = forms.CharField(max_length=100)
    email = forms.EmailField()
    message = forms.CharField(widget=forms.Textarea)
  1. SQLAlchemy
from sqlalchemy import create_engine
engine = create_engine('sqlite:///example.db', echo=True)
  1. CSRF middleware: Django/Flask has built-in CSRF protection.
  2. Email header injection: Ensure email headers are properly encoded and validated.

C#

  1. ASP.NET Validation Controls
<asp:TextBox ID="EmailTextBox" runat="server" />
<asp:RegularExpressionValidator
    ControlToValidate="EmailTextBox"
    ValidationExpression=".*@.*\..*"
    ErrorMessage="Invalid email"
    runat="server" />
  1. Parameterized Queries (Using ADO.NET)
string query = "INSERT INTO Users (Name, Email) VALUES (@Name, @Email)";
SqlCommand command = new SqlCommand(query, connection);
command.Parameters.AddWithValue("@Name", name);
command.Parameters.AddWithValue("@Email", email);
command.ExecuteNonQuery();
  1. AntiForgery tokens: Utilize ASP.NET’s AntiForgery tokens for CSRF protection.
  2. Input encoding: Encode user inputs to prevent XSS attacks.

General security tips

  1. Error Handling (example in Node.js)
app.use((err, req, res, next) => {
    console.error(err.stack);
    res.status(500).send('Something broke!');
});
  1. Security headers (example in Express.js):
const helmet = require('helmet');
app.use(helmet.contentSecurityPolicy({
    directives: {
        defaultSrc: ["'self'"],
        // other directives
    }
}));
  1. Logging: Maintain logs for monitoring and debugging purposes, but ensure they don’t contain sensitive data.

Testing emails with Mailtrap Testing

Email Testing is another inseparable part of Mailtrap Email Delivery Platform, which provides you with a safe environment to inspect emails in staging (web development and QA environments) without the risk of spamming anybody. 

Here’s what you get: 

Now, why would you want to test the emails you’re getting from a form?

Simply, there are three key reasons:

  1. Understand if the form emails are displaying properly.  
  2. See if the emails include all the relevant data. 
  3. Make sure none of the data got compromised in transit. 

Also, I want to stress that you can integrate Mailtrap with SMTP or API. Here’s how to do it, and I assume that you’re already a user. 

SMTP integration

I’ll use the Laravel example to show you how it goes. If you’re on a default Laravel setup, you can just change the values in the ‘.env’ file in the root directory of your project.

MAIL_MAILER=smtp
MAIL_HOST=sandbox.smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=your_mailtrap_username
MAIL_PASSWORD=your_mailtrap_password 

The same principle applies to any other language/framework you’re using. It’s just changing the SMTP credentials – host endpoint, port, username, and password – from Email Sending to Email Testing. 

And remember that there’s an in-app drop-down with 20+ ready-made integrations, you can just copy-paste. 

API integration

You can use the API to fetch emails, check their content, sender, recipient, and other parameters programmatically.

First, you need to get your API token located under Settings > API Tokens

Then, you can use the token to authenticate your API requests. Check two basic examples of API usage. They’re in PHP:

Fetch inbox attributes:

<?php

$curl = curl_init();

curl_setopt_array($curl, [
  CURLOPT_URL => "https://mailtrap.io/api/accounts/account_id/inboxes/inbox_id",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 30,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "GET",
  CURLOPT_HTTPHEADER => [
    "Accept: application/json",
    "Api-Token: 123"
  ],
]);

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
  echo "cURL Error #:" . $err;
} else {
  echo $response;
}

Show email message: 

<?php

$curl = curl_init();

curl_setopt_array($curl, [
  CURLOPT_URL => "https://mailtrap.io/api/accounts/account_id/inboxes/inbox_id/messages/message_id",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 30,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "GET",
  CURLOPT_HTTPHEADER => [
    "Accept: application/json",
    "Api-Token: 123"
  ],
]);

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
  echo "cURL Error #:" . $err;
} else {
  echo $response;
}

If you need more examples and API actions – check the official docs here

Wrapping up

Seemingly simple, to me, the HTML email form is a fascinating intersection of design, functionality, and security. I hope to have helped you get a much better understanding of how to create and implement a form that handles data safely. Then, send that data to your email. 

And yes, the trickery is in all the intricacies of client and server-side scripting. But mark my words, when you see it all up and running smoothly, the joy of success is immense. 

Article by Veljko Ristić Content Manager @ Mailtrap

Linguist by trade, digital marketer at heart, I’m a Content Manager who’s been in the online space for 10+ years. From ads to e-books, I’ve covered it all as a writer, editor, project manager, and everything in between. Now, my passion is with email infrastructure with a strong focus on technical content and the cutting-edge in programming logic and flows. But I still like spreading my gospels while blogging purely about marketing.