Status: Stable ✅
The Email feature provides server-side email utilities and client-side flow components for verification, magic links, password resets, and team invitations. Works standalone or integrates with @fyit/crouton-auth.
The email feature is included in the core @fyit/crouton package. Ensure it is in your extends:
// nuxt.config.ts
export default defineNuxtConfig({
extends: ['@fyit/crouton']
})
# Required
RESEND_API_KEY=re_xxx
# Optional (can be set in nuxt.config.ts)
EMAIL_FROM=noreply@example.com
EMAIL_FROM_NAME=My App
EMAIL_REPLY_TO=support@example.com
// nuxt.config.ts
export default defineNuxtConfig({
extends: ['@fyit/crouton'],
runtimeConfig: {
email: {
resendApiKey: process.env.RESEND_API_KEY,
from: 'noreply@example.com',
fromName: 'My App',
replyTo: 'support@example.com'
},
public: {
crouton: {
email: {
brand: {
name: 'My App',
logoUrl: 'https://example.com/logo.png',
primaryColor: '#0F766E',
url: 'https://example.com'
},
verification: {
codeLength: 6,
codeExpiry: 10, // minutes
resendCooldown: 60 // seconds
},
magicLink: {
expiry: 10, // minutes
resendCooldown: 60 // seconds
}
}
}
}
}
})
import { useEmailService } from '#crouton-email/server/utils/email'
const emailService = useEmailService()
// Send raw email
const result = await emailService.send({
to: 'user@example.com',
subject: 'Hello',
html: '<p>Hello World</p>'
})
import {
sendVerificationEmail,
sendMagicLink,
sendPasswordReset,
sendTeamInvite,
sendWelcome
} from '#crouton-email/server/utils/senders'
// Send verification code email
await sendVerificationEmail({
to: 'user@example.com',
code: '123456',
name: 'John'
})
// Send magic link
await sendMagicLink({
to: 'user@example.com',
link: 'https://app.com/auth/magic?token=xxx',
name: 'John'
})
// Send password reset
await sendPasswordReset({
to: 'user@example.com',
link: 'https://app.com/auth/reset?token=xxx',
name: 'John'
})
// Send team invitation
await sendTeamInvite({
to: 'user@example.com',
link: 'https://app.com/invite/accept?token=xxx',
inviterName: 'Jane',
teamName: 'Acme Inc',
role: 'member'
})
// Send welcome email
await sendWelcome({
to: 'user@example.com',
name: 'John',
getStartedLink: 'https://app.com/getting-started'
})
Complete verification code input flow with resend functionality.
<template>
<EmailVerificationFlow
:email="userEmail"
@verified="handleVerified"
@resend="handleResend"
@error="handleError"
/>
</template>
| Prop | Type | Default | Description |
|---|---|---|---|
email | string | required | Email being verified |
codeLength | number | 6 | Code length |
resendCooldown | number | 60 | Cooldown in seconds |
loading | boolean | false | Loading state while verifying |
error | string | '' | External error message |
| Event | Payload | Description |
|---|---|---|
verified | code: string | Code entered |
resend | - | Resend requested |
error | error: string | Error occurred |
"Check your email" message with resend option.
<template>
<EmailMagicLinkSent
:email="userEmail"
@resend="handleResend"
@change-email="handleChangeEmail"
/>
</template>
| Prop | Type | Default | Description |
|---|---|---|---|
email | string | required | Email where link was sent |
resendCooldown | number | 60 | Cooldown in seconds |
loading | boolean | false | Loading state while resending |
error | string | '' | External error message |
Timer-based resend button with cooldown.
<template>
<EmailResendButton
:cooldown="60"
:loading="isResending"
@resend="handleResend"
/>
</template>
Email input with validation.
<template>
<EmailInput
v-model="email"
:error="emailError"
placeholder="Enter your email"
/>
</template>
Built-in Vue Email templates:
| Template | Purpose | Props |
|---|---|---|
BaseLayout.vue | Shared layout | brandName, logoUrl, primaryColor |
Verification.vue | Verification code | code, name, expiryMinutes |
MagicLink.vue | Magic link login | link, name, expiryMinutes |
PasswordReset.vue | Password reset | link, name, expiryMinutes |
TeamInvite.vue | Team invitation | link, inviterName, teamName, role |
Welcome.vue | Welcome email | name, getStartedLink |
When using with @fyit/crouton-auth, the auth package automatically uses email utilities for:
export default defineNuxtConfig({
extends: ['@fyit/crouton']
// Email config will be used automatically by auth
})
Works without auth. Implement your own endpoints:
// server/api/auth/verify.post.ts
export default defineEventHandler(async (event) => {
const { email } = await readBody(event)
// Generate your verification code
const code = generateCode()
// Save code to your database
await saveVerificationCode(email, code)
// Send email using crouton-email
const { sendVerificationEmail } = await import('#crouton-email/server/utils/senders')
await sendVerificationEmail({ to: email, code })
return { success: true }
})
Create your own templates:
server/emails/MyEmail.vueBaseLayout component for consistent styling<!-- server/emails/OrderConfirmation.vue -->
<script setup lang="ts">
// Note: This path works via filesystem resolution but is not an explicit
// package.json export of @fyit/crouton-email. It relies on the layer's
// file structure being accessible at build time.
import BaseLayout from '@fyit/crouton-email/server/emails/BaseLayout.vue'
defineProps<{
orderNumber: string
items: Array<{ name: string; price: number }>
total: number
}>()
</script>
<template>
<BaseLayout brand-name="My Store">
<h1>Order Confirmed!</h1>
<p>Order #{{ orderNumber }}</p>
<!-- ... -->
</BaseLayout>
</template>
Use Resend's test mode:
# Use Resend's test API key for development
RESEND_API_KEY=re_test_xxx