Study Sync uses Gmail SMTP to send email notifications, including study reminders, deadline alerts, and sharing invitations.
Overview
The email service is built with Nodemailer and supports:
- Deadline reminder emails
- Daily study reminders
- Study plan sharing invitations
- Custom reminder notifications
- Test emails for configuration verification
Configuration file: src/lib/email.js
Prerequisites
- Gmail account
- 2-Factor Authentication enabled (required for App Passwords)
Setup Instructions
Enable 2-Factor Authentication
Gmail App Passwords require 2FA to be enabled on your account.
- Go to Google Account Security
- Under “Signing in to Google”, click “2-Step Verification”
- Follow the setup process to enable 2FA
- Verify it’s working by signing in with 2FA
You cannot create App Passwords without 2-Factor Authentication enabled.
Generate App Password
- Go to Google App Passwords
- You may need to sign in again
- Under “Select app”, choose “Mail”
- Under “Select device”, choose “Other (Custom name)”
- Enter a name like “Study Sync” or “Study Sync Dev”
- Click “Generate”
- Copy the 16-character password (shown without spaces)
Save this password immediately. Google will only show it once. If you lose it, you’ll need to generate a new one.
Configure Environment Variables
Add your Gmail credentials to .env.local:Important:
- Use the App Password, not your regular Gmail password
- The App Password is 16 characters with no spaces
- These are server-side variables (no
NEXT_PUBLIC_ prefix)
Verify Configuration
Test your email configuration by sending a test email through the application or API.
Email Service Implementation
The email service is implemented in src/lib/email.js:
import nodemailer from "nodemailer";
/**
* Email service using Gmail SMTP
* For sending reminder emails and notifications
*/
// Gmail SMTP transporter
const gmailTransporter =
process.env.GMAIL_USER && process.env.GMAIL_APP_PASSWORD
? nodemailer.createTransport({
service: "gmail",
auth: {
user: process.env.GMAIL_USER,
pass: process.env.GMAIL_APP_PASSWORD,
},
})
: null;
Graceful Degradation
The service gracefully handles missing configuration:
export async function sendReminderEmail(to, subject, htmlContent) {
if (!gmailTransporter) {
console.warn("Gmail not configured, skipping email send");
return { success: false, message: "Email service not configured" };
}
try {
const info = await gmailTransporter.sendMail({
from: `The Study Sync <${process.env.GMAIL_USER}>`,
to,
subject,
html: htmlContent,
});
console.log("Email sent via Gmail:", info.messageId);
return { success: true, data: info };
} catch (error) {
console.error("Gmail send error:", error);
return { success: false, error: error.message };
}
}
Email Templates
Study Sync includes several pre-built email templates:
1. Test Email
Verify email configuration:
import { sendTestEmail } from '@/lib/email';
const result = await sendTestEmail('[email protected]');
Sends a simple test message to verify SMTP configuration is working.
2. Deadline Reminder
Notify users about approaching deadlines:
import { sendDeadlineReminder } from '@/lib/email';
const instanceData = {
customTitle: "JavaScript Course",
studyPlanTitle: "Web Development",
deadline: new Date('2026-03-15'),
id: 'instance123'
};
const result = await sendDeadlineReminder('[email protected]', instanceData);
Email includes:
- Study plan title
- Deadline date
- Link to view study plan
- Styled HTML template
3. Daily Reminder
Encourage daily study habits:
import { sendDailyReminder } from '@/lib/email';
const result = await sendDailyReminder(
'[email protected]',
'John Doe',
3 // number of active instances
);
Email includes:
- Personalized greeting
- Count of active study plans
- Link to view all instances
4. Share Invitation
Notify users when a study plan is shared with them:
import { sendShareInvitation } from '@/lib/email';
const result = await sendShareInvitation(
'[email protected]',
'Jane Smith', // sharer's name
'Advanced React Patterns', // plan title
'plan123', // plan ID
'editor' // role: 'viewer' or 'editor'
);
Email includes:
- Sharer’s name
- Study plan title
- Access level (viewer/editor)
- Link to study plan
5. Custom Reminder
User-configured reminder notifications:
import { sendCustomReminder } from '@/lib/email';
const instanceData = {
customTitle: "Python Bootcamp",
studyPlanTitle: "Programming",
deadline: new Date('2026-03-20'),
_id: 'instance456'
};
const result = await sendCustomReminder(
'[email protected]',
instanceData,
'1 day' // reminder type: "1 day before", "2 hours before", etc.
);
Email includes:
- Study plan title
- Time until deadline
- Exact deadline date/time
- Link to study plan
Email Template Styling
All emails use inline CSS for maximum compatibility:
const htmlContent = `
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
<h1 style="color: #3b82f6;">📚 Study Plan Reminder</h1>
<p>Your study plan <strong>${title}</strong> has a deadline approaching.</p>
<a href="${url}"
style="display: inline-block; background-color: #3b82f6; color: white;
padding: 10px 20px; text-decoration: none; border-radius: 5px;">
View Study Plan
</a>
</div>
`;
Styling features:
- Maximum width 600px for readability
- Brand colors (#3b82f6 blue)
- Responsive design
- Clear call-to-action buttons
- Professional footer with disclaimer
Usage Example
Implementing email notifications in an API route:
import { sendDeadlineReminder } from '@/lib/email';
import { getDb } from '@/lib/mongodb';
export default async function handler(req, res) {
try {
const db = await getDb();
// Find instances with deadlines in the next 24 hours
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
const instances = await db.collection('instances')
.find({
deadline: { $lte: tomorrow },
reminderSent: { $ne: true }
})
.toArray();
// Send reminders
for (const instance of instances) {
const user = await db.collection('users')
.findOne({ _id: instance.userId });
if (user.email && user.notificationsEnabled) {
const result = await sendDeadlineReminder(user.email, instance);
if (result.success) {
// Mark reminder as sent
await db.collection('instances').updateOne(
{ _id: instance._id },
{ $set: { reminderSent: true } }
);
}
}
}
res.json({ success: true, count: instances.length });
} catch (error) {
res.status(500).json({ error: error.message });
}
}
Rate Limits
Gmail has sending limits to prevent spam:
Free Gmail Accounts
- 500 emails per day
- 500 recipients per day (each recipient counts separately)
- ~100 recipients per message (for bulk sends)
Google Workspace Accounts
- 2,000 emails per day
- 10,000 recipients per day
- Better deliverability for transactional emails
If you exceed these limits, Gmail will temporarily block your account from sending emails. Consider using a dedicated email service for high-volume applications.
Troubleshooting
”Invalid login: 535-5.7.8 Username and Password not accepted”
Cause: Incorrect credentials or using regular password instead of App Password.
Solutions:
- Verify you’re using an App Password, not your regular Gmail password
- Check 2-Factor Authentication is enabled
- Generate a new App Password and try again
- Ensure
GMAIL_USER and GMAIL_APP_PASSWORD are set correctly
Cause: Environment variables not set.
Solution: Add GMAIL_USER and GMAIL_APP_PASSWORD to .env.local and restart server.
Emails Going to Spam
Possible causes:
- New Gmail account with low sending reputation
- High volume of emails sent quickly
- Generic or spammy email content
Solutions:
- Start with low sending volume and gradually increase
- Use descriptive subject lines
- Include clear sender information
- Add unsubscribe links
- Consider using a dedicated email service (SendGrid, AWS SES)
“Network Error” or Timeout
Possible causes:
- Firewall blocking SMTP port (587 or 465)
- Server network restrictions
Solutions:
- Verify SMTP ports are not blocked
- Check server firewall rules
- Try alternative SMTP ports
Production Considerations
For High-Volume Applications
If you need to send more than 500 emails/day, consider dedicated email services:
- SendGrid - 100 emails/day free, then paid plans
- AWS SES - $0.10 per 1,000 emails
- Mailgun - 5,000 emails/month free
- Postmark - Focused on transactional emails
Migration to SendGrid Example
import sgMail from '@sendgrid/mail';
sgMail.setApiKey(process.env.SENDGRID_API_KEY);
export async function sendEmail(to, subject, html) {
const msg = {
to,
from: '[email protected]', // Verified sender
subject,
html,
};
try {
await sgMail.send(msg);
return { success: true };
} catch (error) {
return { success: false, error: error.message };
}
}
Email Verification
For production, verify recipient email addresses:
- Send confirmation email on signup
- Only send notifications to verified emails
- Provide easy unsubscribe mechanism
- Honor user notification preferences
Monitoring
Track email delivery:
const result = await sendReminderEmail(to, subject, html);
// Log delivery status
await db.collection('emailLogs').insertOne({
to,
subject,
success: result.success,
messageId: result.data?.messageId,
error: result.error,
timestamp: new Date()
});
Security Best Practices
- Use App Passwords: Never use your main Gmail password
- Separate accounts: Use different Gmail account for dev/prod
- Rotate credentials: Change App Passwords periodically
- Validate recipients: Ensure email addresses are valid
- Rate limiting: Implement application-level rate limits
- Sanitize content: Prevent email injection attacks
- Monitor usage: Track sending patterns for anomalies
Testing Emails in Development
Option 1: Send to Your Own Email
const result = await sendTestEmail('[email protected]');
console.log('Test email result:', result);
Option 2: Use Mailtrap (Recommended)
Mailtrap captures emails without sending them:
const testTransporter = nodemailer.createTransport({
host: "smtp.mailtrap.io",
port: 2525,
auth: {
user: process.env.MAILTRAP_USER,
pass: process.env.MAILTRAP_PASS,
},
});
Option 3: Ethereal Email
Automatically generated test accounts:
const testAccount = await nodemailer.createTestAccount();
const transporter = nodemailer.createTransport({
host: 'smtp.ethereal.email',
port: 587,
auth: {
user: testAccount.user,
pass: testAccount.pass,
},
});
Environment Variables Summary
# Gmail SMTP Configuration
GMAIL_USER=[email protected]
GMAIL_APP_PASSWORD=abcdefghijklmnop
# App URL (used in email links)
NEXT_PUBLIC_APP_URL=https://yourdomain.com
Resources