Simple email sending with pre-configured SMTP - no setup required! 🚀
A lightweight wrapper around Nodemailer that automatically detects SMTP settings based on your email provider. Perfect for when you want Resend-like simplicity but need to use free SMTP services.
- ✨ Zero Configuration - Auto-detects SMTP settings for Gmail, Outlook, Yahoo, and more
- 🎯 Resend-like API - Familiar interface if you've used Resend
- 📦 Lightweight - Just a thin wrapper around Nodemailer
- 🔒 TypeScript - Full TypeScript support
- 🆓 Free - Uses your existing email account
npm install maildropimport { drop } from 'maildrop';
// Set environment variables:
// MAILDROP_EMAIL=your-email@gmail.com
// MAILDROP_PASSWORD=your-app-password
const { data, error } = await drop({
to: 'recipient@example.com',
subject: 'Hello World',
html: '<strong>It works!</strong>',
});
if (error) {
return console.error({ error });
}
console.log({ data });import { quickDrop } from 'maildrop';
const { data, error } = await quickDrop(
'your-email@gmail.com',
'your-app-password',
{
to: 'recipient@example.com',
subject: 'Hello World',
html: '<strong>It works!</strong>',
}
);import { MailDrop } from 'maildrop';
const mail = new MailDrop('your-email@gmail.com', 'your-app-password');
const { data, error } = await mail.send({
from: 'Your Name <your-email@gmail.com>',
to: ['recipient@example.com'],
subject: 'Hello World',
html: '<strong>It works!</strong>',
});The following email providers are automatically detected:
- ✅ Gmail (
gmail.com,googlemail.com) - ✅ Outlook (
outlook.com,hotmail.com,live.com,msn.com) - ✅ Yahoo (
yahoo.com,yahoo.co.uk,ymail.com) - ✅ Zoho (
zoho.com,*.zoho.com) - ✅ Custom SMTP (see below)
Create a .env file:
MAILDROP_EMAIL=your-email@gmail.com
MAILDROP_PASSWORD=your-app-passwordThen use:
import { drop } from 'maildrop';
// 'from' is optional - defaults to MAILDROP_EMAIL
const { data, error } = await drop({
to: 'recipient@example.com',
subject: 'Hello',
html: '<p>Hello!</p>',
});Important: Gmail requires an App Password instead of your regular password.
- Enable 2-Step Verification on your Google Account
- Generate an App Password: Google Account Settings
- Use the generated app password (16 characters, no spaces)
import { MailDrop } from 'maildrop';
const mail = new MailDrop('your-email@gmail.com', 'your-16-char-app-password');
const { data, error } = await mail.send({
from: 'Your Name <your-email@gmail.com>',
to: 'recipient@example.com',
subject: 'Hello from Gmail!',
html: '<p>This email was sent using Gmail SMTP.</p>',
});const mail = new MailDrop('your-email@outlook.com', 'your-password');
const { data, error } = await mail.send({
from: 'Your Name <your-email@outlook.com>',
to: ['recipient@example.com'],
subject: 'Hello from Outlook!',
html: '<p>This email was sent using Outlook SMTP.</p>',
});For providers not automatically detected, you can provide custom SMTP settings:
const mail = new MailDrop(
'user@example.com',
'password',
{
host: 'smtp.example.com',
port: 587,
secure: false, // true for 465, false for other ports
}
);const { data, error } = await mail.send({
from: 'sender@example.com',
to: ['recipient1@example.com', 'recipient2@example.com'],
cc: ['cc@example.com'],
bcc: ['bcc@example.com'],
subject: 'Hello',
html: '<p>Hello everyone!</p>',
});const { data, error } = await mail.send({
from: 'sender@example.com',
to: 'recipient@example.com',
subject: 'Hello',
text: 'Plain text version',
html: '<p>HTML version</p>',
});Test your SMTP connection before sending:
const mail = new MailDrop('your-email@gmail.com', 'your-password');
const isValid = await mail.verify();
if (isValid) {
console.log('SMTP connection is valid!');
} else {
console.error('SMTP connection failed. Check your credentials.');
}Drop an email using environment variables. No credentials needed in code!
drop(options: Omit<SendEmailOptions, 'from'> & { from?: string }): Promise<SendEmailResponse>Environment Variables Required:
MAILDROP_EMAIL- Your email addressMAILDROP_PASSWORD- Your email password/app-password
Options:
to(string | string[]) - Recipient email address(es) - requiredsubject(string) - Email subject - requiredhtml(string, optional) - HTML contenttext(string, optional) - Plain text contentfrom(string, optional) - Sender email address (defaults toMAILDROP_EMAIL)cc(string | string[], optional) - CC recipientsbcc(string | string[], optional) - BCC recipientsreplyTo(string, optional) - Reply-to address
Example:
import { drop } from 'maildrop';
const { data, error } = await drop({
to: 'recipient@example.com',
subject: 'Hello',
html: '<p>Hello!</p>',
});Drop an email without class instantiation.
quickDrop(email: string, password: string, options: Omit<SendEmailOptions, 'from'> & { from?: string }): Promise<SendEmailResponse>Parameters:
email- Your email addresspassword- Your email password/app-passwordoptions- Email options (same asdrop,fromdefaults toemail)
Example:
import { quickDrop } from 'maildrop';
const { data, error } = await quickDrop(
'user@gmail.com',
'app-password',
{
to: 'recipient@example.com',
subject: 'Hello',
html: '<p>Hello!</p>',
}
);new MailDrop(email: string, password: string, customSMTP?: SMTPConfig)email- Your email addresspassword- Your email password or app-specific passwordcustomSMTP- Optional custom SMTP configuration
Send an email.
Options:
from(string) - Sender email addressto(string | string[]) - Recipient email address(es)subject(string) - Email subjecthtml(string, optional) - HTML contenttext(string, optional) - Plain text contentcc(string | string[], optional) - CC recipientsbcc(string | string[], optional) - BCC recipientsreplyTo(string, optional) - Reply-to address
Returns:
{
data?: {
id: string;
messageId: string;
};
error?: {
message: string;
code?: string;
};
}Verify SMTP connection. Returns true if connection is valid.
maildrop supports sending to multiple recipients in a single call. You can use arrays for to, cc, and bcc fields:
const { data, error } = await drop({
to: ['user1@example.com', 'user2@example.com', 'user3@example.com'],
subject: 'Newsletter',
html: '<p>Hello everyone!</p>',
});For small batches (< 50 emails):
- Use arrays directly in
to,cc, orbcc - All recipients will see each other's addresses (use
bccfor privacy)
For larger batches (> 50 emails):
- Use
bccto hide recipient addresses from each other - Consider sending in smaller batches with delays to avoid rate limits
- Use a queue system for production applications
// Good: Use BCC for privacy
const { data, error } = await drop({
to: 'your-email@example.com', // Your address in 'to'
bcc: ['user1@example.com', 'user2@example.com', /* ... many more */],
subject: 'Newsletter',
html: '<p>Hello!</p>',
});
// Better: Send in batches for large lists
const recipients = [/* large array */];
const batchSize = 50;
for (let i = 0; i < recipients.length; i += batchSize) {
const batch = recipients.slice(i, i + batchSize);
await drop({
bcc: batch,
subject: 'Newsletter',
html: '<p>Hello!</p>',
});
// Small delay to avoid rate limits
await new Promise(resolve => setTimeout(resolve, 1000));
}Rate Limits:
- Gmail: ~500 emails/day for free accounts, ~2000/day for Google Workspace
- Outlook: ~300 emails/day for free accounts
- Yahoo: ~500 emails/day
For production bulk email, consider using dedicated email services (SendGrid, Mailgun, etc.) or upgrade to a business email account.
For custom SMTP servers, you can provide additional configuration options:
const mail = new MailDrop(
'user@example.com',
'password',
{
host: 'smtp.example.com',
port: 587, // 587 for TLS, 465 for SSL
secure: false, // true for SSL (port 465), false for TLS (port 587)
}
);- 587 - TLS (STARTTLS) - Most common, recommended
- 465 - SSL - Legacy but still widely used
- 25 - Unencrypted - Not recommended, often blocked
- 2525 - Alternative TLS port (some providers)
Many enterprise email providers use custom SMTP settings:
// Example: Custom corporate email
const mail = new MailDrop(
'user@company.com',
'password',
{
host: 'mail.company.com', // Your company's mail server
port: 587,
secure: false,
}
);Note: maildrop uses Nodemailer under the hood, so you can access the full Nodemailer API if needed by accessing the transporter directly (though this requires modifying the source code).
Always check for errors:
const { data, error } = await mail.send({
from: 'sender@example.com',
to: 'recipient@example.com',
subject: 'Hello',
html: '<p>Hello</p>',
});
if (error) {
console.error('Failed to send email:', error.message);
// Handle error
return;
}
console.log('Email sent successfully:', data.messageId);When sending to multiple recipients, handle errors gracefully:
const recipients = ['user1@example.com', 'user2@example.com', 'user3@example.com'];
const results = [];
for (const recipient of recipients) {
const { data, error } = await drop({
to: recipient,
subject: 'Hello',
html: '<p>Hello!</p>',
});
if (error) {
console.error(`Failed to send to ${recipient}:`, error.message);
results.push({ recipient, success: false, error: error.message });
} else {
results.push({ recipient, success: true, messageId: data?.messageId });
}
}
console.log(`Sent ${results.filter(r => r.success).length}/${recipients.length} emails`);- Make sure you're using an App Password, not your regular Gmail password
- Enable 2-Step Verification first
- Generate a new App Password if needed
- Make sure you're using your full email address as the username
- Some accounts may require enabling "Less secure app access" (not recommended)
- Consider using an App Password instead
- Check your firewall settings
- Verify the SMTP port (587 for TLS, 465 for SSL)
- Some networks block SMTP ports
- Free email accounts have daily sending limits
- Sending too many emails too quickly can trigger spam filters
- If your account gets suspended, wait 24 hours before trying again
- For production, consider using a dedicated email service
Always use environment variables for credentials in production:
// ✅ Good: Using environment variables
const { data, error } = await drop({
to: process.env.ADMIN_EMAIL,
subject: 'System Alert',
html: '<p>Alert message</p>',
});
// ❌ Bad: Hardcoded credentials
const { data, error } = await quickDrop(
'my-email@gmail.com',
'my-password', // Never do this!
{ /* ... */ }
);Implement retry logic for production applications:
async function sendWithRetry(options: SendEmailOptions, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
const { data, error } = await drop(options);
if (!error) {
return { data, error: null };
}
// Don't retry on authentication errors
if (error.code === 'EAUTH' || error.message.includes('password')) {
return { data: null, error };
}
// Wait before retrying (exponential backoff)
if (attempt < maxRetries) {
await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
}
}
return { data: null, error: { message: 'Max retries exceeded' } };
}For applications sending many emails, use a queue system:
// Example using a simple queue
const emailQueue: Array<SendEmailOptions> = [];
async function processQueue() {
while (emailQueue.length > 0) {
const email = emailQueue.shift();
if (email) {
const { error } = await drop(email);
if (error) {
console.error('Failed to send:', error);
// Add to retry queue or log for manual review
}
// Rate limiting: wait between sends
await new Promise(resolve => setTimeout(resolve, 100));
}
}
}Log email sends for debugging and monitoring:
async function sendWithLogging(options: SendEmailOptions) {
const startTime = Date.now();
const { data, error } = await drop(options);
const duration = Date.now() - startTime;
if (error) {
console.error('[Email] Failed:', {
to: options.to,
error: error.message,
duration,
});
} else {
console.log('[Email] Sent:', {
to: options.to,
messageId: data?.messageId,
duration,
});
}
return { data, error };
}Use maildrop when:
- ✅ Sending transactional emails (welcome emails, password resets, notifications)
- ✅ Low to medium volume (< 1000 emails/day)
- ✅ Personal projects or small applications
- ✅ You want to use your existing email account
Consider dedicated services when:
- ❌ High volume (> 1000 emails/day)
- ❌ Marketing campaigns or newsletters
- ❌ Need advanced analytics and tracking
- ❌ Need guaranteed delivery rates
- ❌ Enterprise requirements
Popular alternatives: SendGrid, Mailgun, AWS SES, Postmark, Resend
ISC
Contributions welcome! Feel free to open issues or submit pull requests.