The Nuxt Email Layer includes powerful templating capabilities using Vue components and the Vue Email library. Create beautiful, responsive, and maintainable email templates with full TypeScript support.
Email templates are regular Vue components that use specialized email components from the Vue Email library. They're rendered server-side into HTML that's compatible with email clients.
The layer includes several pre-built email templates ready for use:
Welcome Email - welcome.vue - User onboardingEmail Verification - email-verification.vue - Account verificationPassword Reset - password-reset.vue - Password recoveryTwo-Factor Auth - two-factor-auth.vue - 2FA codesLogin Alert - login-alert.vue - Security notificationsAccount Deactivation - account-deactivation.vue - Account closure
Import and use templates in your server-side code:
server/api/send-welcome.ts
import WelcomeEmail from '#layers/email/server/emails/welcome.vue'
export default defineEventHandler ( async ( event ) => {
const email = useEmail ()
return await email. send ({
to: 'user@example.com' ,
subject: 'Welcome to Our Platform!' ,
template: WelcomeEmail,
data: {
userName: 'John Doe' ,
userEmail: 'user@example.com' ,
dashboardUrl: 'https://app.example.com/dashboard' ,
supportUrl: 'https://help.example.com'
}
})
})
Create your own email templates using Vue components and Vue Email components.
server/emails/custom-email.vue
< script setup lang = "ts" >
import {
Html,
Head,
Preview,
Container,
Section,
Heading,
Text,
Button,
Link
} from '@vue-email/components'
// Define props with TypeScript
defineProps ({
userName: {
type: String,
required: true
},
actionUrl: {
type: String,
required: true
}
})
</ script >
< template >
< Html lang = "en" >
< Head / >
< Preview >Welcome to our platform, {{ userName }}!</ Preview >
< Container style = "max-width: 600px; margin: 0 auto; font-family: Arial, sans-serif;" >
< Section style = "padding: 20px;" >
< Heading style = "color: #333; font-size: 24px;" >
Hello {{ userName }}!
</ Heading >
< Text style = "color: #666; line-height: 1.6;" >
Welcome to our platform. We're excited to have you on board!
</ Text >
< Button
:href = "actionUrl"
style = "background: #007bff; color: white; padding: 12px 24px; border-radius: 4px; text-decoration: none;"
>
Get Started
</ Button >
</ Section >
</ Container >
</ Html >
</ template >
server/api/send-custom.ts
import CustomEmail from '~/server/emails/custom-email.vue'
export default defineEventHandler ( async ( event ) => {
const email = useEmail ()
return await email. send ({
to: 'user@example.com' ,
subject: 'Custom Email' ,
template: CustomEmail,
data: {
userName: 'Jane Doe' ,
actionUrl: 'https://example.com/action'
}
})
})
The layer includes the full Vue Email component library. Here are the most commonly used components:
< template >
< Html lang = "en" >
< Head / >
< Preview >Email preview text</ Preview >
< Container style = "max-width: 600px; margin: 0 auto;" >
< Section style = "padding: 20px;" >
<!-- Content here -->
</ Section >
</ Container >
</ Html >
</ template >
< template >
< Heading
as = "h1"
style = "color: #333; font-size: 28px; font-weight: bold;"
>
Main Heading
</ Heading >
< Text style = "color: #666; font-size: 16px; line-height: 1.5;" >
Body text content goes here.
</ Text >
< Link
href = "https://example.com"
style = "color: #007bff; text-decoration: none;"
>
Click here
</ Link >
</ template >
< template >
< Button
href = "https://example.com/action"
style = "
background: #007bff;
color: white;
padding: 12px 24px;
border-radius: 4px;
text-decoration: none;
display: inline-block;
"
>
Call to Action
</ Button >
< Hr style = "border: none; border-top: 1px solid #eee; margin: 20px 0;" />
< Img
src = "https://example.com/logo.png"
alt = "Company Logo"
width = "120"
height = "40"
style = "display: block; margin: 0 auto;"
/>
</ template >
server/emails/order-confirmation.vue
< script setup lang = "ts" >
import {
Html, Head, Preview, Container, Section,
Heading, Text, Button, Hr, Link
} from '@vue-email/components'
interface OrderItem {
name : string
quantity : number
price : number
}
defineProps ({
customerName: {
type: String,
required: true
},
orderNumber: {
type: String,
required: true
},
orderItems: {
type: Array as PropType < OrderItem []>,
required: true
},
totalAmount: {
type: Number,
required: true
},
trackingUrl: {
type: String,
required: true
}
})
</ script >
< template >
< Html lang = "en" >
< Head / >
< Preview >Order #{{ orderNumber }} confirmed - Thank you for your purchase!</ Preview >
< Container style = "max-width: 600px; margin: 0 auto; font-family: Arial, sans-serif;" >
<!-- Header -->
< Section style = "background: #f8f9fa; padding: 20px; text-align: center;" >
< Heading style = "color: #333; margin: 0;" >
Order Confirmed!
</ Heading >
</ Section >
<!-- Content -->
< Section style = "padding: 30px;" >
< Text style = "color: #333; font-size: 18px;" >
Hi {{ customerName }},
</ Text >
< Text style = "color: #666; line-height: 1.6;" >
Thank you for your order! We've received your payment and your order is being processed.
</ Text >
<!-- Order Details -->
< Section style = "background: #f8f9fa; padding: 20px; margin: 20px 0; border-radius: 8px;" >
< Heading as = "h3" style = "color: #333; margin: 0 0 15px 0;" >
Order #{{ orderNumber }}
</ Heading >
< div v-for = "item in orderItems" :key = "item.name" style = "margin-bottom: 10px;" >
< Text style = "margin: 0; color: #333;" >
{{ item.quantity }}x {{ item.name }} - ${{ item.price.toFixed(2) }}
</ Text >
</ div >
< Hr style = "border: none; border-top: 1px solid #ddd; margin: 15px 0;" />
< Text style = "margin: 0; color: #333; font-weight: bold;" >
Total: ${{ totalAmount.toFixed(2) }}
</ Text >
</ Section >
< Button
:href = "trackingUrl"
style = "
background: #28a745;
color: white;
padding: 15px 30px;
border-radius: 6px;
text-decoration: none;
font-weight: bold;
display: inline-block;
"
>
Track Your Order
</ Button >
</ Section >
<!-- Footer -->
< Section style = "background: #f8f9fa; padding: 20px; text-align: center;" >
< Text style = "color: #999; font-size: 14px; margin: 0;" >
Questions? Reply to this email or
< Link href = "mailto:support@example.com" style = "color: #007bff;" >
contact support
</ Link >
</ Text >
</ Section >
</ Container >
</ Html >
</ template >
server/emails/newsletter.vue
< script setup lang = "ts" >
import {
Html, Head, Preview, Container, Section,
Heading, Text, Button, Img, Hr, Link
} from '@vue-email/components'
interface Article {
title : string
excerpt : string
url : string
image ?: string
}
defineProps ({
subscriberName: {
type: String,
required: true
},
articles: {
type: Array as PropType < Article []>,
required: true
},
unsubscribeUrl: {
type: String,
required: true
}
})
</ script >
< template >
< Html lang = "en" >
< Head / >
< Preview >Your weekly newsletter - Latest articles and updates</ Preview >
< Container style = "max-width: 600px; margin: 0 auto; font-family: Arial, sans-serif;" >
<!-- Header -->
< Section style = "background: #007bff; color: white; padding: 30px; text-align: center;" >
< Heading style = "color: white; margin: 0;" >
Weekly Newsletter
</ Heading >
< Text style = "color: #cce7ff; margin: 5px 0 0 0;" >
{{ new Date().toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric'
}) }}
</ Text >
</ Section >
<!-- Greeting -->
< Section style = "padding: 30px 30px 20px 30px;" >
< Text style = "color: #333; font-size: 18px; margin: 0;" >
Hi {{ subscriberName }},
</ Text >
< Text style = "color: #666; line-height: 1.6;" >
Here are this week's top articles and updates from our blog.
</ Text >
</ Section >
<!-- Articles -->
< Section style = "padding: 0 30px;" >
< div v-for = "(article, index) in articles" :key = "index" >
< Section style = "margin-bottom: 30px;" >
< Img
v-if = "article.image"
:src = "article.image"
:alt = "article.title"
width = "540"
height = "200"
style = "width: 100%; height: 200px; object-fit: cover; border-radius: 8px;"
/>
< Heading
as = "h3"
style = "color: #333; margin: 15px 0 10px 0; font-size: 20px;"
>
< Link :href = "article.url" style = "color: #333; text-decoration: none;" >
{{ article.title }}
</ Link >
</ Heading >
< Text style = "color: #666; line-height: 1.6; margin: 0 0 15px 0;" >
{{ article.excerpt }}
</ Text >
< Button
:href = "article.url"
style = "
background: #007bff;
color: white;
padding: 10px 20px;
border-radius: 4px;
text-decoration: none;
font-size: 14px;
"
>
Read More
</ Button >
</ Section >
< Hr
v-if = "index < articles.length - 1"
style = "border: none; border-top: 1px solid #eee; margin: 20px 0;"
/>
</ div >
</ Section >
<!-- Footer -->
< Section style = "background: #f8f9fa; padding: 30px; text-align: center; margin-top: 30px;" >
< Text style = "color: #999; font-size: 14px; margin: 0 0 10px 0;" >
You're receiving this because you subscribed to our newsletter.
</ Text >
< Link :href = "unsubscribeUrl" style = "color: #999; font-size: 12px;" >
Unsubscribe from these emails
</ Link >
</ Section >
</ Container >
</ Html >
</ template >
Organize your templates in a logical structure:
server/
├── emails/
│ ├── auth/
│ │ ├── welcome.vue
│ │ ├── email-verification.vue
│ │ └── password-reset.vue
│ ├── orders/
│ │ ├── confirmation.vue
│ │ └── shipping.vue
│ └── marketing/
│ ├── newsletter.vue
│ └── promotion.vue
Inline styles : Email clients have limited CSS support, so use inline stylesTable-based layouts : Use Vue Email components for reliable renderingWeb fonts : Stick to web-safe fonts for maximum compatibilityImages : Always include alt text and fallback contentUse TypeScript interfaces to ensure type safety:
server/emails/typed-template.vue
< script setup lang = "ts" >
interface User {
id : string
name : string
email : string
}
interface TemplateProps {
user : User
actionUrl : string
expiresAt : Date
}
const props = defineProps < TemplateProps >()
</ script >
Make your emails mobile-friendly:
< template >
< Container style = "max-width: 600px; margin: 0 auto;" >
< Section style = "padding: 20px;" >
<!-- Use percentage widths for mobile -->
< div style = "width: 100%; max-width: 300px; margin: 0 auto;" >
< Button
href = "https://example.com"
style = "
width: 100%;
padding: 15px;
background: #007bff;
color: white;
text-align: center;
display: block;
"
>
Full Width Button
</ Button >
</ div >
</ Section >
</ Container >
</ template >
Use the email devtools to preview your templates:
Send test emails using your templates View them in the devtools at /__email-devtools Test different data combinations Create a test API endpoint for template development:
server/api/test-template.ts
import MyTemplate from '~/server/emails/my-template.vue'
export default defineEventHandler ( async ( event ) => {
const query = getQuery (event)
const email = useEmail ()
return await email. send ({
to: 'test@example.com' ,
subject: 'Template Test' ,
template: MyTemplate,
data: {
userName: query.name || 'Test User' ,
// Add other test data
}
})
})