Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/revokslab/shipfree/llms.txt

Use this file to discover all available pages before exploring further.

ShipFree includes a provider-agnostic email system that supports multiple email services through a unified interface. Send transactional emails, marketing campaigns, and more with automatic provider fallback.

Architecture

The email system uses a mailer abstraction that automatically selects the best available provider:
src/lib/messaging/email/mailer.ts
import { getProvider, getFirstAvailableProvider } from './providers'

function getConfiguredProvider(): EmailProvider {
  const explicitName = env.EMAIL_PROVIDER?.toLowerCase()
  
  // Use explicit provider if set
  if (explicitName) {
    const explicitProvider = getProvider(explicitName)
    if (explicitProvider) return explicitProvider
  }
  
  // Auto-discover first available provider
  return getFirstAvailableProvider()
}
The system automatically falls back to console logging if no provider is configured, making development easier.

Supported Providers

Resend

Modern email API with excellent deliverability (recommended)

Postmark

Transactional email service with detailed analytics

Plunk

Simple email API for developers

Nodemailer

SMTP support for any email provider

Provider Priority

If EMAIL_PROVIDER is not set, the system auto-discovers providers in this order:
  1. Custom (if injected via setCustomEmailProvider)
  2. Resend (if RESEND_API_KEY is set)
  3. Postmark (if POSTMARK_API_KEY is set)
  4. Plunk (if PLUNK_API_KEY is set)
  5. Nodemailer (if SMTP credentials are set)
  6. Log (console fallback for development)

Configuration

.env
EMAIL_PROVIDER=resend
RESEND_API_KEY=re_...
EMAIL_FROM_ADDRESS=noreply@yourdomain.com
EMAIL_FROM_NAME="Your App Name"
Resend is the recommended provider for ShipFree due to its simplicity and reliability.

Sending Emails

The email system provides a simple API for sending emails:

Basic Usage

import { sendEmail } from '@/lib/messaging/email'

await sendEmail({
  to: 'user@example.com',
  subject: 'Welcome to ShipFree',
  html: '<h1>Welcome!</h1><p>Thanks for signing up.</p>',
  emailType: 'transactional',
})

With React Email Templates

1

Create Email Template

Email templates are located in src/components/emails/:
src/components/emails/auth/welcome-email.tsx
import { Html, Head, Body, Container, Heading, Text } from '@react-email/components'

interface WelcomeEmailProps {
  name?: string
}

export function WelcomeEmail({ name = 'there' }: WelcomeEmailProps) {
  return (
    <Html>
      <Head />
      <Body>
        <Container>
          <Heading>Welcome to ShipFree!</Heading>
          <Text>Hi {name},</Text>
          <Text>Thanks for signing up. We're excited to have you.</Text>
        </Container>
      </Body>
    </Html>
  )
}
2

Render Template

Use the render function to convert React components to HTML:
src/components/emails/render.ts
import { render } from '@react-email/components'
import { WelcomeEmail } from './auth/welcome-email'

export async function renderWelcomeEmail(name?: string) {
  return await render(<WelcomeEmail name={name} />)
}
3

Send Email

import { sendEmail } from '@/lib/messaging/email'
import { renderWelcomeEmail, getEmailSubject } from '@/components/emails'

const html = await renderWelcomeEmail(user.name)

await sendEmail({
  to: user.email,
  subject: getEmailSubject('welcome'),
  html,
  from: 'noreply@yourdomain.com',
  emailType: 'transactional',
})

Email Types

ShipFree distinguishes between two email types:
Transactional emails are triggered by user actions and contain critical information:
  • Password reset emails
  • Email verification
  • Order confirmations
  • Account notifications
await sendEmail({
  to: user.email,
  subject: 'Reset Your Password',
  html: resetPasswordHtml,
  emailType: 'transactional', // Default
})
Transactional emails do not include unsubscribe links and should only be used for essential communications.

Built-in Email Templates

ShipFree includes pre-built email templates for authentication flows:

Welcome Email

Sent after email verification

Password Reset

Secure password reset flow

OTP Verification

One-time password codes

Example: OTP Email

src/lib/auth/auth.ts
import { renderOTPEmail, getEmailSubject } from '@/components/emails'
import { sendEmail, getFromEmailAddress } from '@/lib/messaging/email'

emailOTP({
  sendVerificationOTP: async ({ email, otp, type }) => {
    const html = await renderOTPEmail(otp, email, type)
    
    const result = await sendEmail({
      to: email,
      subject: getEmailSubject(type),
      html,
      from: getFromEmailAddress(),
      emailType: 'transactional',
    })
    
    if (!result.success) {
      throw new Error(`Failed to send verification code: ${result.message}`)
    }
  },
})

Batch Emails

Send multiple emails efficiently:
import { sendBatchEmails } from '@/lib/messaging/email'

const result = await sendBatchEmails({
  emails: [
    {
      to: 'user1@example.com',
      subject: 'Welcome!',
      html: '<p>Welcome user 1</p>',
    },
    {
      to: 'user2@example.com',
      subject: 'Welcome!',
      html: '<p>Welcome user 2</p>',
    },
  ],
})

console.log(result.message) // "2/2 emails sent successfully"
If the provider supports native batch sending (like Resend), it will be used automatically. Otherwise, emails are sent sequentially.

Email Validation

Validate email addresses before sending:
import { validateEmail, quickValidateEmail } from '@/lib/messaging/email'

// Quick validation (format only)
const quick = quickValidateEmail('user@example.com')
if (!quick.isValid) {
  console.error(quick.reason)
}

// Full validation (includes DNS checks)
const full = await validateEmail('user@example.com')
if (!full.isValid) {
  console.error('Invalid email:', full.reason)
}

Server-Side Usage

src/app/(auth)/verify/page.tsx
import { sendEmail } from '@/lib/messaging/email'
import { renderWelcomeEmail } from '@/components/emails'

export default async function VerifyPage() {
  // Send welcome email after verification
  const sendWelcomeEmail = async (user: User) => {
    const html = await renderWelcomeEmail(user.name)
    
    await sendEmail({
      to: user.email,
      subject: 'Welcome to ShipFree!',
      html,
      emailType: 'transactional',
    })
  }
  
  return <div>Email sent!</div>
}

Check Email Service Status

import { hasEmailService, getActiveProviderName } from '@/lib/messaging/email'

if (hasEmailService()) {
  console.log('Email provider:', getActiveProviderName())
} else {
  console.warn('No email service configured')
}

Attachments

Send files with emails:
import { sendEmail } from '@/lib/messaging/email'

await sendEmail({
  to: 'user@example.com',
  subject: 'Your Invoice',
  html: '<p>Please find your invoice attached.</p>',
  attachments: [
    {
      filename: 'invoice.pdf',
      content: pdfBuffer,
      contentType: 'application/pdf',
    },
  ],
})

Reply-To Headers

await sendEmail({
  to: 'customer@example.com',
  subject: 'Support Request',
  html: '<p>We received your support request.</p>',
  replyTo: 'support@yourdomain.com',
})

Custom Provider

Inject a custom email provider at runtime:
import { setCustomEmailProvider } from '@/lib/messaging/email'
import type { EmailProvider } from '@/lib/messaging/email'

const myProvider: EmailProvider = {
  name: 'custom',
  send: async (data) => {
    // Custom sending logic
    return { success: true, message: 'Email sent' }
  },
}

setCustomEmailProvider(myProvider)

Error Handling

The email system provides detailed error information:
import { sendEmail } from '@/lib/messaging/email'

const result = await sendEmail({
  to: 'invalid-email',
  subject: 'Test',
  html: '<p>Test</p>',
})

if (!result.success) {
  console.error('Email failed:', result.message)
  // Handle error (retry, log, notify admin, etc.)
}
Always check the success property before assuming an email was sent successfully.

Development Mode

Without any email provider configured, emails are logged to the console:
🔑 VERIFICATION CODE FOR LOGIN/SIGNUP
{
  email: 'user@example.com',
  otp: '123456',
  type: 'sign-in'
}
This makes it easy to test authentication flows locally without setting up an email provider.

Best Practices

Use Templates

Always use React Email templates for consistent branding

Validate First

Validate email addresses before sending to reduce bounces

Handle Errors

Always check send results and implement retry logic

Respect Privacy

Include unsubscribe links for marketing emails

Further Reading

React Email

Build beautiful emails with React components

Resend Docs

Resend API documentation

Postmark Docs

Postmark developer guides

Email Best Practices

Learn about email deliverability

Build docs developers (and LLMs) love