DEV Community

Tanay Karnik
Tanay Karnik

Posted on • Originally published at saas-boilerplate.dev

Sending Emails in Nuxt 3: How I Handle Emails in My SaaS Boilerplate

If you're building anything serious in Nuxt 3, you'll need to send emails.
Here's my setup that lets you switch between Resend, SendGrid, or any other provider without rewriting code.

The Setup

Basic Configuration

const generateMailer = async () => {
  const mailer = await generateMailer({
    default: "resend",
    from: {
      address: "hi@saas-boilerplate.dev",
      name: "SaaS Boilerplate",
    },
    mailers: {
      resend: transports.resend({
        key: process.env.RESEND_API_KEY || "",
        baseUrl: "https://api.resend.com",
      }),
      // Easy to add more providers!
      sendgrid: transports.sendgrid({
        key: process.env.SENDGRID_API_KEY || "",
      })
    },
  });

  return mailer;
};
Enter fullscreen mode Exit fullscreen mode

Email Templates with Vue Email

Here's a simple magic link email template:

<template>
  <Html>
    <Body>
      <Container>
        <Heading>Sign In to Your Account</Heading>
        <Text>Hi {{ username }},</Text>
        <Text>Click the button below to sign in:</Text>
        <Section>
          <Button :href="signInLink">Sign In</Button>
        </Section>
        <Text>
          Or copy this link: 
          <Link :href="signInLink">{{ signInLink }}</Link>
        </Text>
        <Hr />
        <Text>This link expires in 1 hour.</Text>
      </Container>
    </Body>
  </Html>
</template>
<script setup lang="ts">
import {
  Body, Button, Container, Heading,
  Html, Link, Section, Text, Hr
} from "@vue-email/components";

interface Props {
  username?: string;
  signInLink?: string;
}

withDefaults(defineProps<Props>(), {
  username: "User",
  signInLink: "https://app.example.com/sign-in",
});
</script>
Enter fullscreen mode Exit fullscreen mode

Sending Emails

import MagicLinkSignIn from "~/emails/MagicLinkSignIn.vue";
const mailer = await getMailer();
await mailer.send(async (message) => {
  message
    .to(email)
    .subject("Magic Link Sign In")
    .html(
      await render(MagicLinkSignIn, {
        username: user?.name,
        signInLink: url,
      })
    );
});
Enter fullscreen mode Exit fullscreen mode

Cool Features from AdonisJS Mail

AdonisJS Mail comes with tons of powerful features that we can use:

  1. Multiple Transport Support: Switch between email providers with one line of code
  2. Calendar Events: Attach calendar invites to your emails
  3. File Attachments: Easily attach files, streams, or buffers
  4. HTML/Text Templates: Support for both HTML and plain text emails

For example, attaching a calendar invite is as simple as:

message.icalEvent((calendar) => {
  calendar.createEvent({
    summary: 'Team Meeting',
    start: DateTime.local().plus({ minutes: 30 }),
    end: DateTime.local().plus({ minutes: 60 }),
  })
});
Enter fullscreen mode Exit fullscreen mode

Why this works well

  • Switch providers anytime
  • Write templates in Vue
  • Easy to test
  • All the power of AdonisJS Mail in any JS backend

Try it yourself

Want to learn more about what AdonisJS Mail can do? Check out their detailed documentation.

This email setup is part of my Nuxt SaaS boilerplate.

If you're building a SaaS, check it out—it comes with type-safe APIs using tRPC, enterprise-grade RBAC (role-based access control), and production-ready features like auth, team management, and billing.

Every feature is built with the same attention to developer experience as this email system.

Top comments (0)