Subject lines
Compute a subject line from the same typed props with defineEmail, and read it back from the render result.
Nuxt Email does not own subjects — sending is your provider's job — but a template often knows how to phrase its own subject from the same data it renders. defineEmail lets a template declare that subject, computed from its props, and surface it on the render result.
Declare a subject
Call defineEmail in the template's <script setup>. It is an explicit server-only import from @lupinum/nuxt-email/define-email.
<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>
<EHtml lang="en">
<EHead><title>Activate your account</title></EHead>
<EBody>
<EPreview>Your account is ready.</EPreview>
<EContainer>
<EHeading>Welcome, {{ firstName }}</EHeading>
<EButton :href="activationUrl" :style="{ padding: '12px 20px' }">Activate</EButton>
</EContainer>
</EBody>
</EHtml>
</template>The zero-argument subject closure captures the template's real defineProps() value and returns the subject string. There is no separate generic prop declaration that can drift from the SFC.
Read it back
When a template calls defineEmail, the render result carries a subject:
export default defineEventHandler(async () => {
const { html, text, subject } = await renderEmail('welcome', {
firstName: 'Ada',
activationUrl: 'https://example.com/activate',
})
// subject === 'Welcome aboard, Ada'
return { html, text, subject }
})subject is string | undefined on RenderedEmail — it is absent when the template does not call defineEmail.
Rules and edge cases
- Runs during render only. Calling
defineEmailoutside an email render throwsDefineEmailOutsideRenderError. - One declaration per render. Calling it twice in one template render throws
DuplicateEmailDefinitionError. - Returns a string. A non-string subject fails the render with an
EmailRenderErrorwhose cause explains the invalid return value. - Works before or after
await. The render context is propagated withAsyncLocalStorage, so declaring the subject after a top-levelawaitin<script setup>(for example after fetching data) still resolves to the correct render. - Concurrent renders are isolated. Each render has its own context, so subjects never leak between simultaneous renders.
subject is a convenience for keeping subject phrasing beside the template. You still hand it to your provider SDK alongside from, to, html, and text — Nuxt Email does not send mail or set headers.