DEV Community

Tanay Karnik
Tanay Karnik

Posted on • Originally published at saas-boilerplate.dev

2 1 1 1 1

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)

Image of Docusign

🛠️ Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay