Article

Sending Email from an HTML Form with Vercel and Resend

A practical step-by-step guide to securely sending emails from a static HTML form using Vercel serverless functions and Resend.

Published VercelResendEmailHTMLServerless

Introduction

When deploying static or frontend-heavy applications on Vercel, a common requirement is handling form submissions — especially sending emails — without running a traditional backend.

This post documents a clean, production-ready setup using:

  • A plain HTML form
  • A Vercel Serverless Function
  • Resend as the email provider

This approach keeps API keys secure, avoids client-side email hacks, and fits perfectly within Vercel’s hosting model.


Architecture Overview

HTML Form
   ↓ (POST)
Vercel API Route (/api/contact)

Resend Email API

Your Inbox

Key principles:

  • No secrets in the browser
  • No PHP or custom backend
  • Works with static HTML, Astro, Next.js, etc.

Prerequisites

  • A Vercel project
  • Node.js 18+
  • A Resend account

Step 1 — Install Resend

npm install resend

Step 2 — Create the Serverless Email Endpoint

Create the following file in your project:

api/contact.ts
import { Resend } from "resend";

const resend = new Resend(process.env.RESEND_API_KEY);

export default async function handler(req, res) {
  if (req.method !== "POST") {
    return res.status(405).json({ error: "Method not allowed" });
  }

  const { name, email, message } = req.body;

  if (!name || !email || !message) {
    return res.status(400).json({ error: "Missing required fields" });
  }

  try {
    await resend.emails.send({
      from: "Contact Form <onboarding@resend.dev>",
      to: ["you@yourdomain.com"],
      reply_to: email,
      subject: `New contact from ${name}`,
      text: `
Name: ${name}
Email: ${email}

${message}
      `,
    });

    return res.status(200).json({ success: true });
  } catch (error) {
    return res.status(500).json({ error: "Failed to send email" });
  }
}

Step 3 — Configure Environment Variables

In the Vercel Dashboard:

Project → Settings → Environment Variables

Add:

RESEND_API_KEY=your_resend_api_key

Redeploy your project after adding the variable.


Step 4 — Plain HTML Form

This works with pure HTML, no framework required.

<form id="contact-form">
  <input name="name" placeholder="Your name" required />
  <input name="email" type="email" placeholder="Your email" required />
  <textarea name="message" placeholder="Message" required></textarea>
  <button type="submit">Send</button>
</form>

<script>
  const form = document.getElementById("contact-form");

  form.addEventListener("submit", async (e) => {
    e.preventDefault();

    const formData = new FormData(form);
    const data = Object.fromEntries(formData.entries());

    const response = await fetch("/api/contact", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(data),
    });

    if (response.ok) {
      alert("Message sent successfully");
      form.reset();
    } else {
      alert("Failed to send message");
    }
  });
</script>

Security & Production Tips

  • Never expose API keys in the browser
  • ✅ Add a honeypot field for spam protection
  • ✅ Add rate limiting if the form is public
  • ✅ Consider a custom from domain in Resend

Conclusion

By combining Vercel serverless functions with Resend, you get a simple, secure, and modern way to handle email from HTML forms — without compromising security or developer experience.

If you’re already deploying on Vercel, this is one of the most frictionless solutions available today.