Typed, name-checked rendering
renderEmail('welcome', props) is generated from app/emails/. Unknown names, missing props, and wrong types fail at compile time.
Author typed Vue single-file components with Tailwind utilities, then render deterministic HTML, plain text, and subject lines from the same Nitro-native path.
<script setup lang="ts">
import { defineEmail } from '@lupinum/nuxt-email/define-email'
const props = defineProps<{
firstName: string
activationUrl: string
}>()
defineEmail({
subject: () => `Welcome aboard, ${props.firstName}`,
})
</script>
<template>
<ETailwind>
<EHtml lang="en">
<EHead>
<title>Activate your account</title>
</EHead>
<EBody class="m-0 bg-slate-100 p-6">
<EPreview>Your account is ready.</EPreview>
<EContainer class="rounded-lg bg-white p-6">
<EHeading class="m-0 text-2xl text-slate-900">Welcome, {{ firstName }}</EHeading>
<EText class="text-slate-600">Finish setting up your account.</EText>
<EButton class="rounded-md bg-blue-600 px-5 py-3 text-white" :href="activationUrl">
Activate account
</EButton>
</EContainer>
</EBody>
</EHtml>
</ETailwind>
</template>renderEmail('welcome', props) is generated from app/emails/. Unknown names, missing props, and wrong types fail at compile time.
ETailwind compiles utilities into inline styles and preserves the head rules email clients need, including classes from nested Vue components.
Two renders of the same template and props return byte-identical HTML and a matching plain-text fallback. No IDs, timestamps, or hydration payload.
Eighteen built-in E* components handle email structure and styling; configured applications can add syntax-highlighted ECodeBlock.
Open /__email in dev to inspect preview, exact HTML, and plain text with sibling fixtures. Preview routes and fixtures never reach production.
Covered behaviors are compared with a fixed React Email 6.9.0 version. A generated report records intentional differences.