Sending Emails with Astro v3 and Nodemailer
3 min read
This post focuses on how to send emails in an Astro v3 project using TypeScript and Nodemailer.
Setup
# Initialize Astro project and install dependencies
pnpm create astro@latest && cd <project_name> && pnpm i
Install Packages
# Install Nodemailer for sending emails and Zod for validation
pnpm i nodemailer zod
Environment Variables
Create a .env
file and add your credentials see this to learn how to get it :
MY_EMAIL=yourappemail@gmail.com
MY_PASSWORD=yourapppassword
Email Utility Function
Create a file src/lib/utils.ts
and add:
import * as nodemailer from "nodemailer";
const MY_EMAIL = import.meta.env.MY_EMAIL;
const MY_PASSWORD = import.meta.env.MY_PASSWORD;
export const sendEmail = async ({
name,
email,
message,
}: {
name: string;
email: string;
message: string;
}): Promise<void> => {
const transporter = nodemailer.createTransport({
service: "gmail",
host: "smtp.gmail.com",
port: 587,
secure: false,
auth: {
user: MY_EMAIL,
pass: MY_PASSWORD,
},
});
const mailOptions = {
from: MY_EMAIL,
to: "youEmail@gmail.com", # wirte you email address here
subject: `Contact form submission from ${name}`,
text: `Email: ${email}\nMessage: ${message}`,
};
await transporter.sendMail(mailOptions);
};
Building the Validation Function
Let’s start by defining our validation function using Zod. Create a new file under /src/lib/utils
and add the following code:
import { z } from 'zod';
export const formDataSchema = z.object({
name: z
.string()
.min(3, { message: "Name must be at least 3 characters long" }),
email: z
.string()
.email({ message: "Invalid email address" }),
message: z
.string()
.min(8, { message: "Message must be at least 8 characters long" }),
});
export type FormData = z.infer<typeof formDataSchema>;
Form UI
Update src/pages/index.astro
to handle the form submission we can start by building the ui:
<Layout description='email with astro demo' title="email with astro">
<main class="text-primary-50 flex-1 bg-primary-500 w-full h-screen flex justify-center items-center rounded-xl p-6">
<form method="POST" class="flex flex-col w-full gap-4">
<input type="text" name="name" class="w-full rounded-lg h-12 px-2 bg-primary-600 placeholder:text-light-100" placeholder="Name" />
{errors.name && <p>{errors.name}</p>}
<input type="email" name="email" class="w-full rounded-lg h-12 px-2 bg-primary-600 placeholder:text-light-100" placeholder="email" required />
{errors.email && <p>{errors.email}</p>}
<label>
<textarea name="message" required minlength="6" class="w-full rounded-lg h-36 resize-y px-2 bg-primary-600 placeholder:text-light-100" name="message" placeholder="Message" />
</label>
{errors.message && <p>{errors.message}</p>}
<button class="h-12 w-full rounded-xl bg-light-50 dark:bg-dark-900 dark:text-light-50 text-dark-900" >Register</button>
</form>
</main>
</Layout>
Building the contact form logic
After that, we can start building the contact form logic. Place the following code at the top of src/pages/index.astro
:
---
import { z } from "astro:content";
import { sendEmail, formDataSchema } from "../lib/utils";
import Layout from "../layouts/Layout.astro";
let errors = { name: "", email: "", message: "" };
if (Astro.request.method === "POST") {
try {
const data = await Astro.request.formData();
const name = data.get("name");
const email = data.get("email");
const message = data.get("message");
const formData = formDataSchema.parse({
name,
email,
message,
});
await sendEmail(formData).catch((error) => {
console.error(`An error occurred: ${error}`);
});
} catch (error) {
if (error instanceof z.ZodError) {
console.error(`Validation error: ${error}`);
} else {
console.error(`An error occurred: ${error}`);
}
}
}
---
The last steps
While nodemailer
can only run in a Node.js runtime, Astro is a static site generator (SSG) by default. To switch Astro to server-side rendering (SSR) mode, you can run the following command in a Node.js environment:
pnpm astro add node
However, if you wish to deploy it serverlessly on Vercel, use this command instead:
pnpm astro add vercel
For more information on this subject, check out the Astro SSR guide.
That’s it! You can now easily send emails directly from your Astro project using Nodemailer.
Happy coding!