Tasks

Send an email

Send a branded email from a Vue template with sendEmailTemplate, and pick the delivery driver.

What you'll build: a welcome email, written as a Vue component and sent from a server route. In development it prints in the terminal, and outside development a real provider delivers it.

Prerequisites

The paths below are for the starter's apps/web.

1. Add Email Templates

Notify sends the email, and Email Templates turns a Vue component into its HTML. Install Email Templates in the app:

pnpm --filter ./apps/web add @frigear-nu/email-templates
List Email Templates in modules yourself. Being a dependency of another module is not enough: the templates of those modules do not render.
apps/web/nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@frigear-nu/notify', '@frigear-nu/email-templates'],
  notify: { email: { driver: 'log' } },
})

2. Write the template

A template is a Vue component, so you write the email with the same tools as a page. Every top-level .vue file in app/emails is a template, named by its file name:

apps/web/app/emails/welcome.vue
<script setup lang="ts">
import { EmailButton, EmailDivider, EmailHeading, EmailLayout, EmailLinkFallback, EmailText } from '@frigear-nu/email-templates/components'

defineProps<{ name: string, url: string }>()
</script>

<template>
  <EmailLayout :preview="`Welcome, ${name}!`">
    <ESubject>Welcome, {{ name }}</ESubject>
    <EmailHeading>Welcome, {{ name }}!</EmailHeading>
    <EmailDivider />
    <EmailText>Thanks for signing up. Click the button below to get started.</EmailText>
    <EmailButton :href="url">Get started</EmailButton>
    <EmailLinkFallback :href="url" />
  </EmailLayout>
</template>
  • <ESubject> sets the subject.
  • Import the Email* components. Only the renderer's own E* components are global.

3. Send it

sendEmailTemplate is auto-imported in server code. The template name and its props are typed, so a misspelled name or a missing prop does not compile:

apps/web/server/api/welcome.post.ts
export default defineEventHandler(async (event) => {
  const { user } = await requireAuth(event, { sessionOnly: true })
  await sendEmailTemplate('welcome', { name: user.name, url: 'https://example.com/account' }, {
    to: user.email,
    replyTo: 'support@frigear.nu',
  })
  return { sent: true }
})

The third argument is an EmailNotification without body, plus three optional fields:

FieldPurpose
subjectOverrides <ESubject>. Required when the template has none.
textYour own plain text, or false for HTML only.
localeThe locale to render the template in.
To send without a template, call dispatchEmailNotification({ to, subject, body, text }) from Notify.
What you should see: signed in, POST /api/welcome answers { "sent": true }, and the terminal running pnpm dev prints the email.

4. Choose a driver

The log driver prints emails to the terminal. That is the safe default for development. Switch drivers per environment with variables, which win over nuxt.config.ts:

VariablePurpose
NUXT_NOTIFY_EMAIL_DRIVERlog (default), resend, ses or cloudflare.
NUXT_NOTIFY_EMAIL_FROMThe sender address, for example Frigear <no-reply@frigear.nu>.
NUXT_NOTIFY_EMAIL_RESEND_API_TOKENThe API token, with the resend driver.

ses and cloudflare have their own credentials. A missing one fails the send, with an error that names the variable.

Next steps

  • Sell event tickets – send this email from the events:ticket:paid hook.
  • Email Templates – use the components and branding, override the module emails by file name, and preview them in DevTools.
  • Notify – send SMS and simple notifications, or write your own driver.
Copyright © 2026