blog
Send Email with JavaScript: Browser, Node.js, and Email API Patterns
Send email with JavaScript from Node.js or a browser-backed API without exposing credentials. Includes Nodemailer, MailSlurp, and delivery checks.

JavaScript can send email, but the safe method depends on where the code runs. A browser should hand the request to your backend. Node.js can send through SMTP or an email API because it can keep credentials private.
This guide gives you working examples for both paths, then shows how to prove the message arrived instead of trusting a successful send call.
Short answer
- Browsers cannot securely send SMTP email directly with raw sockets.
mailto:works for simple contact actions, but it depends on the user's local email client.- For production, send email from a backend using Node.js SMTP or a JavaScript email API.
- Keep credentials out of frontend bundles and test full send-receive flows in CI.
Which JavaScript email pattern should you use?
- Open the user's email client: use a
mailto:link. See the HTML mailto guide. - Send from Node.js over SMTP: keep the credentials in server environment variables. Follow the Node SMTP email guide.
- Send through an HTTP API: call the email API from backend code. Start with the MailSlurp send email API.
- Test delivery in CI: use MailSlurp inboxes with wait assertions. See email integration testing.
Use the browser as the trigger, not the place where credentials live. The safest JavaScript email API pattern is a server-side route that validates the request, sends through MailSlurp, and records the result for debugging.
Need the protocol background first? Read SMTP protocol explained and SMTP authentication.
Option 1: use mailto: for lightweight flows
mailto: opens the default email client with pre-filled fields.
<a href="mailto:support@example.com?subject=Support%20request">Email support</a>
Use it when:
- you only need a quick user-triggered email draft
- you do not need delivery guarantees or analytics
Limitations:
- no server-side delivery control
- inconsistent behavior across devices
- not suitable for transactional automation
Option 2: send email with JavaScript on Node.js (SMTP)
Server-side JavaScript can send SMTP mail safely with nodemailer.
npm install nodemailer
import nodemailer from "nodemailer";
const transporter = nodemailer.createTransport({
host: process.env.SMTP_HOST,
port: 587,
secure: false,
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS,
},
});
await transporter.sendMail({
from: "no-reply@example.com",
to: "user@example.com",
subject: "Welcome",
text: "Your account is ready.",
});
Use this when your app already has backend infrastructure and SMTP requirements.
For the full server-side implementation path, use the Node SMTP email guide.
Related: Nodemailer tutorial and SMTP relay guide.
Option 3: send email from Node.js with an email API
Install the MailSlurp JavaScript client:
npm install mailslurp-client
Then create a sender inbox and send the message from server-side JavaScript:
import { MailSlurp } from "mailslurp-client";
const apiKey = process.env.MAILSLURP_API_KEY;
const recipient = process.env.RECIPIENT_EMAIL;
if (!apiKey || !recipient) {
throw new Error("Set MAILSLURP_API_KEY and RECIPIENT_EMAIL");
}
const mailslurp = new MailSlurp({ apiKey });
const sender = await mailslurp.createInbox();
if (!sender.id) {
throw new Error("MailSlurp did not return an inbox ID");
}
await mailslurp.sendEmail(sender.id, {
to: [recipient],
subject: "Welcome",
body: "Your account is ready.",
});
Use this pattern in a Node.js worker, server route, test runner, or scheduled job. The API key remains in the server environment, and the sender inbox can be reused or created for a specific test run.
For the endpoint, request fields, HTML, and attachment options, see the MailSlurp send email API.
Option 4: let browser JavaScript call your backend
A clean pattern is: browser JS calls your backend endpoint, backend sends the email.
Frontend:
await fetch("/api/send-email", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
to: "user@example.com",
template: "welcome",
}),
});
The backend validates the request, applies authentication and rate limits, then runs the Node.js SMTP or MailSlurp code above. Return a request or message identifier rather than exposing provider credentials or raw errors to the browser.
This gives you:
- secret management on the server
- validation and abuse controls
- observability and retries
For a complete HTTP example, see the Axios email API example. The JavaScript SendEmailOptions guide covers additional message fields.
Recommended MailSlurp JavaScript email API workflow
MailSlurp gives JavaScript teams both the send path and the proof that the message arrived. A dependable workflow is:
- Create a MailSlurp inbox for the sender, recipient, or test run.
- Send from a backend route, Node.js worker, or CI job using your MailSlurp API key.
- Wait for the received message with a MailSlurp wait helper.
- Assert the subject, body, links, OTP codes, attachments, and headers.
- Store the MailSlurp message ID with your test or release record.
This is useful for signup emails, password resets, magic links, invoice messages, support notifications, and any workflow where a successful send call is not enough.
Common JavaScript email errors (and fixes)
535 Authentication failed
- verify SMTP username/password
- verify STARTTLS/TLS mode and port
- check provider policy (app passwords, tenant settings)
More detail: SMTP authentication errors.
CORS failures when calling send endpoints
- allow only trusted origins
- keep send endpoint server-side
- avoid direct third-party key usage in browser code
Exposed secrets in frontend bundles
- never embed SMTP credentials in client JavaScript
- use server-only env vars
- rotate keys if exposure is suspected
Testing JavaScript email flows end to end
Sending is only half the workflow. This test creates two temporary inboxes, sends between them, and waits for the delivered message. Each inbox expires after ten minutes, and the finally block removes it sooner:
import { MailSlurp } from "mailslurp-client";
const apiKey = process.env.MAILSLURP_API_KEY;
if (!apiKey) {
throw new Error("Set MAILSLURP_API_KEY");
}
const mailslurp = new MailSlurp({ apiKey });
const [sender, recipient] = await Promise.all([
mailslurp.createInboxWithOptions({
name: "JavaScript email test sender",
expiresIn: 10 * 60 * 1000,
}),
mailslurp.createInboxWithOptions({
name: "JavaScript email test recipient",
expiresIn: 10 * 60 * 1000,
}),
]);
if (!sender.id || !recipient.id || !recipient.emailAddress) {
throw new Error("Could not create the test inboxes");
}
try {
await mailslurp.sendEmail(sender.id, {
to: [recipient.emailAddress],
subject: "Verify your account",
body: "Your verification code is 482913.",
});
const received = await mailslurp.waitForLatestEmail(
recipient.id,
30_000,
true,
);
if (received.subject !== "Verify your account") {
throw new Error(`Unexpected subject: ${received.subject}`);
}
if (!received.body?.includes("482913")) {
throw new Error("Verification code was missing");
}
} finally {
await Promise.allSettled([
mailslurp.deleteInbox(sender.id),
mailslurp.deleteInbox(recipient.id),
]);
}
In an application test, create the recipient inbox, enter its address in the real signup or reset flow, then wait for that message. Verify:
- the message arrives
- subject/body content is correct
- links and OTP codes are valid
- timing is within expected SLA
MailSlurp supports on-demand inboxes and wait-for-email assertions for integration tests. Start with sending emails, the JavaScript SDK quickstart, and email integration testing.
Launch checklist for JavaScript email features
Before release, pair send logic with operational checks:
- Validate sender auth and policy posture with SMTP authentication and DMARC monitoring.
- Test render and delivery quality using email client testing and email deliverability testing.
- Keep end-to-end regression coverage in Email Sandbox so flows are verified on every deploy.