renderEmail
The one public render API — its signature, deterministic result shape, plain-text contract, and security boundary.
renderEmail(name, props) is the one public application rendering path. Nuxt generates its template names and prop types from app/emails/, then resolves the selected Vue SFC through the same canonical server-only registry the development preview uses.
const result = await renderEmail('welcome', {
firstName: 'Ada',
activationUrl: 'https://example.com/activate',
})It is a Nitro auto-import — available in server handlers and other Nitro server code, not in Vue components, client plugins, or production client bundles. Do not import it from @lupinum/nuxt-email or #imports.
Result shape
The promise resolves to the canonical RenderedEmail type, available from the package root as a type-only import:
import type { RenderedEmail } from '@lupinum/nuxt-email'html— the rendered document. Begins with the XHTML 1.0 Transitional doctype.text— the plain-text fallback (see below).subject— present only when the template declares one viadefineEmail; otherwise absent.
There is no preview metadata, provider result, diagnostics object, timing, or size field. Sending is not part of this function — your application supplies recipients, sender identity, subject, credentials, and provider-specific options directly to its chosen provider SDK.
HTML contract
- A template must render exactly one
<html>root containing exactly one<body>.EHtmlandEBodyare the supported wrappers; the renderer never repairs or auto-wraps fragments, text roots, body-only templates, or multiple roots. EHeadis optional but supplies the recommended UTF-8 and Apple reformatting meta tags.- Vue SSR interpolation and attribute values are escaped.
- A fresh isolated Vue SSR application is created for every call. Email templates receive E-components through server-only auto-imports.
- Framework code never fetches images, stylesheets, fonts, or other remote assets during rendering.
Determinism: two calls with the same template code and props return byte-identical html and text. Templates remain responsible for avoiding clocks, random values, mutable external state, and nondeterministic data.
Props and generated types
Template names are relative .vue paths under app/emails/, without the extension and with / separators — app/emails/account/reset-password.vue becomes account/reset-password. Files under app/emails/components/ are excluded.
Props declared with ordinary defineProps() or Vue's props option drive both the generated call-site type and runtime validation. TypeScript rejects unknown template names, missing required props, wrong values, and extra props. Untyped runtime calls reject unknown or missing props deterministically before SSR.
There is no public registry API — the generated registry is an internal server artifact and the sole source for rendering, generated types, and preview listings.
Plain-text contract
text is produced from the final rendered HTML with pinned html-to-text behavior and no line wrapping. Head content, images, <script>/<style> content, and elements marked data-skip-in-text="true" are excluded, so EPreview is absent from the fallback. Links retain destinations when the visible text differs. See the plain-text guide for details and the data-text-format="dataTable" opt-in.
Security boundary
Nuxt Email renders trusted application templates with untrusted values supplied only through normal escaped Vue bindings. It is not an HTML sanitizer.
- All E-prefixed primitives reject
innerHTML,textContent, and attributes beginning withon. - There is no raw-HTML component. Do not use
v-htmlor native raw HTML with untrusted content inside a template. hrefandsrcvalues are escaped but URL schemes are not validated. Validate application-controlled URLs before rendering.- When consumed through the canonical generated API, template modules and the renderer are server-only and excluded from the client build. Do not import email templates into client code.
- Development fixture modules, preview UI, and preview endpoints are absent from production builds. Do not import fixture modules into production code.
- No production HTTP render route and no send route are generated. A route exists only when you create one.
The production server intentionally contains templates referenced by its registry. The guarantee is client exclusion, not removal from the server that renders them.