Skip to main content

Transactional email, native to Nuxt.

Author typed Vue single-file components with Tailwind utilities, then render deterministic HTML, plain text, and subject lines from the same Nitro-native path.

app/emails/welcome.vue
<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>

Typed, name-checked rendering

renderEmail('welcome', props) is generated from app/emails/. Unknown names, missing props, and wrong types fail at compile time.

Tailwind v4, inlined

ETailwind compiles utilities into inline styles and preserves the head rules email clients need, including classes from nested Vue components.

Deterministic HTML and plain text

Two renders of the same template and props return byte-identical HTML and a matching plain-text fallback. No IDs, timestamps, or hydration payload.

Outlook-safe primitives

Eighteen built-in E* components handle email structure and styling; configured applications can add syntax-highlighted ECodeBlock.

Development preview

Open /__email in dev to inspect preview, exact HTML, and plain text with sibling fixtures. Preview routes and fixtures never reach production.

Conformance with evidence

Covered behaviors are compared with a fixed React Email 6.9.0 version. A generated report records intentional differences.