Skip to content
GitHubTwitterDiscord

Email System Setup & Testing

LaunchKit includes a comprehensive email system built with Resend and React Email, providing beautiful transactional emails for your SaaS. This guide covers setup, customization, and testing.

The email system includes:

  • 7 Pre-built email templates for common SaaS scenarios
  • React Email components for easy customization
  • Test API endpoint for development and debugging
  • Automatic email triggers from Stripe webhooks and user actions
  1. Go to Resend.com
  2. Sign up for an account
  3. Complete email verification

For production use:

  1. Go to Domains in Resend dashboard

  2. Add your domain (e.g., yourapp.com)

  3. Add DNS records as instructed:

    • MX Record: feedback-mx.resend.co
    • TXT Record: "v=spf1 include:_spf.resend.co ~all"
    • CNAME Record: resend._domainkey.yourapp.com
  4. Wait for DNS propagation and verification

  1. Go to API Keys in Resend dashboard
  2. Click “Create API Key”
  3. Name it (e.g., “LaunchKit Production”)
  4. Copy the key: re_...

Add to your .env.local:

# Resend Configuration
RESEND_API_KEY=re_your_api_key_here

LaunchKit includes 7 professionally designed email templates:

Purchase Confirmation (purchase-confirmation.tsx)

  • Sent after successful payment
  • Includes order details, download links, GitHub access
  • Contains invoice link and next steps

Payment Failed (payment-failed.tsx)

  • Sent when payment is declined
  • Includes retry instructions and support contact
  • Clear call-to-action to update payment method

Invoice Paid (invoice-paid.tsx)

  • Sent for successful recurring payments
  • Contains invoice details and receipt
  • Confirms continued service access

All email templates are in /components/emails/:

components/emails/
├── basic-template.tsx          # Base template
├── checkout-expired.tsx
├── github-access-granted.tsx
├── invoice-paid.tsx
├── new-signup.tsx
├── payment-failed.tsx
├── purchase-confirmation.tsx
├── subscription-cancelled.tsx

Each template is a React component using @react-email/components:

import * as React from 'react';
import {
  Body, Container, Head, Heading, Html, Img, Link,
  Preview, Section, Text, Tailwind, Button, Hr
} from '@react-email/components';

interface EmailProps {
  customerName?: string;
  customerEmail?: string;
  productName?: string;
  // ... other props
}

const EmailTemplate = (props: EmailProps) => {
  const {
    customerName = 'Customer',
    productName = 'Product',
    // ... default values
  } = props;

  return (
    <Html>
      <Head />
      <Preview>Your purchase confirmation for {productName}</Preview>
      <Tailwind>
        <Body className="bg-white font-sans">
          <Container className="max-w-2xl mx-auto py-8">
            {/* Email content */}
          </Container>
        </Body>
      </Tailwind>
    </Html>
  );
};

export default EmailTemplate;

Branding:

// Add your logo
<Img
  src="https://yourdomain.com/logo.png"
  width="120"
  height="40"
  alt="Your App Logo"
  className="mb-6"
/>

// Update colors
<Button
  className="bg-blue-600 text-white px-6 py-3 rounded-lg"
  href={dashboardUrl}
>
  Access Dashboard
</Button>

Content:

// Customize messaging
<Heading className="text-2xl font-bold text-gray-900 mb-4">
  Welcome to {appName}! 🎉
</Heading>

<Text className="text-gray-600 mb-6">
  Thanks for joining thousands of developers who trust {appName}
  to launch their SaaS applications faster.
</Text>

LaunchKit includes a powerful testing endpoint at /api/test-email for debugging and previewing emails.

# Send all 7 email types to default address
GET http://localhost:3000/api/test-email

# Response includes:
{
  "success": true,
  "message": "Test email(s) sent successfully to [email protected]",
  "emailType": "all",
  "results": [
    { "type": "purchase-confirmation", "status": "sent" },
    { "type": "payment-failed", "status": "sent" },
    // ... all templates
  ]
}

Test with Browser:

# Open in browser for easy testing
http://localhost:3000/api/test-email?type=purchase-confirmation&email=[email protected]

Test with cURL:

curl "http://localhost:3000/api/test-email?type=all&[email protected]"

Test with JavaScript:

// Test from browser console or app
fetch('/api/test-email?type=purchase-confirmation&[email protected]')
  .then(response => response.json())
  .then(data => console.log(data));

React Email Dev Server:

# Start email development server
bun run email:dev

# Opens on http://localhost:3001
# Live preview of all email templates
# Hot reload when templates change

Individual Template Testing:

# Test specific template with custom data
fetch('/api/test-email', {
  method: 'GET',
  headers: { 'Content-Type': 'application/json' }
})

Staging Environment:

# Test on staging with real email delivery
GET https://your-staging-app.vercel.app/api/test-email?type=all&email=[email protected]

Webhook-Triggered Emails:

# Complete a test purchase to trigger real emails
# Check email delivery and formatting
# Verify all links work correctly

The EmailService class handles all email sending:

// Located at /libs/email-service.ts
import { EmailService } from '@/libs/email-service';

// Send purchase confirmation
await EmailService.sendPurchaseConfirmation({
  customerName: 'John Doe',
  customerEmail: '[email protected]',
  orderNumber: 'LK-2025-001',
  productName: 'LaunchKit Pro',
  amount: '$99.00',
  // ... other required fields
});

Emails are automatically sent by:

Stripe Webhooks: (/app/api/webhook/stripe/route.ts)

  • Purchase confirmation after successful payment
  • Payment failed notifications
  • Subscription cancellation notices
  • Invoice paid confirmations

User Actions:

  • New user registration
  • GitHub access grants
  • Support ticket responses

Configure sender information in /config.ts:

resend: {
  // Email 'From' field for system emails
  fromNoReply: `YourApp <[email protected]>`,

  // Email 'From' field for personal emails
  fromAdmin: `Your Name at YourApp <[email protected]>`,

  // Support email for customer replies
  supportEmail: '[email protected]',
}

Ensure proper DNS setup for high deliverability:

SPF Record:

v=spf1 include:_spf.resend.co ~all

DKIM Record:

resend._domainkey.yourdomain.com CNAME resend1._domainkey.resend.co

DMARC Record (Optional):

v=DMARC1; p=none; rua=mailto:[email protected]

Subject Lines:

  • Keep under 50 characters
  • Avoid spam trigger words
  • Use personalization: Welcome to YourApp, ${customerName}!

Email Content:

  • Include plain text version
  • Use clear, scannable layouts
  • Test across email clients
  • Include unsubscribe links

Resend Dashboard:

  • Track delivery rates
  • Monitor bounces and complaints
  • View click-through rates
  • Check spam scores

Custom Analytics:

// Track email opens/clicks in your app
const trackEmailOpen = async (emailId: string, userId: string) => {
  await supabase.from('email_analytics').insert({
    email_id: emailId,
    user_id: userId,
    event_type: 'open',
    timestamp: new Date()
  });
};

Emails Not Sending:

# Check API key configuration
echo $RESEND_API_KEY

# Verify domain setup in Resend dashboard
# Check server logs for error messages
# Test with simple API call

Emails Going to Spam:

  • Verify DNS records are correctly configured
  • Check sender reputation in Resend dashboard
  • Avoid spam trigger words in subject/content
  • Include proper unsubscribe mechanisms

Template Rendering Issues:

# Test individual templates
bun run email:dev

# Check React Email component syntax
# Verify all props are properly typed
# Test with default values

Email Testing:

// Add debug logging to EmailService
console.log('Sending email:', {
  to: data.customerEmail,
  subject: `Purchase Confirmed - ${data.productName}`,
  template: 'purchase-confirmation'
});

Resend Logs:

  • Check delivery status in Resend dashboard
  • View bounce and complaint details
  • Monitor API usage and limits

Create automated email sequences:

// Welcome series for new users
const sendWelcomeSeries = async (userId: string) => {
  // Day 0: Welcome email
  await EmailService.sendNewSignup(userData);

  // Day 3: Getting started tips
  setTimeout(() => {
    EmailService.sendGettingStarted(userData);
  }, 3 * 24 * 60 * 60 * 1000);

  // Day 7: Feature highlights
  setTimeout(() => {
    EmailService.sendFeatureHighlights(userData);
  }, 7 * 24 * 60 * 60 * 1000);
};

Use customer data for personalization:

const personalizedEmail = {
  customerName: profile.name || 'there',
  accountAge: getDaysSinceSignup(profile.created_at),
  totalPurchases: await getPurchaseCount(profile.id),
  favoriteFeature: await getMostUsedFeature(profile.id)
};

Test different email versions:

const emailVersion = Math.random() > 0.5 ? 'A' : 'B';

if (emailVersion === 'A') {
  await EmailService.sendPurchaseConfirmationV1(data);
} else {
  await EmailService.sendPurchaseConfirmationV2(data);
}

// Track performance in analytics

Once your email system is configured:

  1. Set up Authentication - Configure user login and OAuth
  2. Test Complete User Journey - End-to-end testing including emails
  3. Configure Admin Dashboard - Monitor email delivery and user engagement
  4. Deploy to Production - Set up production email domain
  • Always test emails before deploying
  • Use the test endpoint extensively during development
  • Preview emails across different devices and clients
  • Keep template code clean and well-documented
  • Monitor delivery rates and engagement metrics
  • Implement proper error handling and retry logic
  • Respect user preferences and unsubscribe requests
  • Maintain professional tone and branding consistency
  • Never include sensitive information in emails
  • Use secure links with proper authentication
  • Implement email verification for critical actions
  • Monitor for suspicious email activity