Overview
For mails supastarter uses Maizzle 6, a framework for building HTML emails with Vue SFCs and Tailwind CSS 4.
Why Maizzle
Maizzle lets you author email templates as Vue single-file components with
Tailwind CSS, then compiles them to clean HTML with inline styles for maximum
email-client compatibility. PostHTML .html templates are not supported.
Providers
In the packages/mail/provider folder you can find the available mail providers.
There are multiple providers available:
To select a provider, update the export in packages/mail/provider/index.ts:
export * from "./resend";
// or export * from './postmark';
// or export * from './mailgun';
// or export * from './nodemailer';
// or export * from './console'; // for debuggingFor debugging purposes you can set the mail provider to console. This will log the mail output only on the console and not send any mails.
Set from mail address
Next up, set the from mail address via the MAIL_FROM environment variable in your .env.local file.
This is the mail address that will be used as the sender of all mails. Please make sure that the mail address and domain are verified in your mail provider.
MAIL_FROM="example@example.com"Mail templates
All your mail templates are located in the packages/mail/emails folder as Vue SFCs (.vue). Shared chrome (Layout, Logo, LinkFallback) lives in packages/mail/components. Preview and build config is in packages/mail/maizzle.config.ts.
You can preview your email templates during development using the mail preview app at http://localhost:3005:
pnpm --filter mail-preview devCreate a mail template
To create a new mail template, create a new .vue file in the packages/mail/emails directory:
<script setup lang="ts">
usePreheader("Your preheader text here");
usePlaintext();
const {
bodyHtml = "Your email content here.",
confirmAction = "Continue",
url = "https://example.com",
openLinkInBrowser = "If you want to open the link in a different browser than your default one, copy and paste this link:",
} = defineProps<{
bodyHtml?: string;
confirmAction?: string;
url?: string;
openLinkInBrowser?: string;
}>();
</script>
<template>
<Layout>
<p class="m-0 mb-6 text-lg/7 text-muted-foreground" v-html="bodyHtml" />
<Button
:href="url"
class="mb-6 rounded-xl bg-primary text-primary-foreground"
>
{{ confirmAction }} →
</Button>
<LinkFallback :href="url">
{{ openLinkInBrowser }}
</LinkFallback>
</Layout>
</template>Templates use Maizzle’s Vue composition helpers (usePreheader, usePlaintext) and shared components from packages/mail/components. Brand colors and type live in the Layout theme block. Send-time values are passed as Maizzle props (not PostHTML expressions).
SaaS ships the Vue sources through Nitro serverAssets (baseName: "mail", {emails,components}/**/*.vue) so serverless renders can materialize layouts at runtime.
Register the mail template
Before you can use your new mail template, you have to register it in the packages/mail/util/templates.ts file,
where you can also define the subject:
export const mailTemplates = {
//...
newMail: {
file: "emails/NewMail.vue",
subject: "New Mail",
},
};Translations
Mail copy lives in packages/i18n/translations/{locale}/mail.json. Add matching keys for every supported locale (en, de, es, fr). buildLocalizedMailContent localizes the subject and template values (including turning body into bodyHtml) before sendEmail merges caller context into Maizzle props.
Use the mail template
Now you can send a mail with your template using the sendEmail() method from the @repo/mail package (server-side only):
import { sendEmail } from "@repo/mail";
await sendEmail({
to: "tim@apple.com",
locale: "en",
templateId: "newMail",
context: {
url: "https://example.com",
},
});