Skip to main content

Preview workflow

Use the development-only /__email preview, add fixtures, and monitor exact rendered size without treating it as a client guarantee.

Nuxt Email exposes /__email while the development server is running. The path follows the configured Nuxt base URL. Open /__email with the default base URL, or /sub/__email with app.baseURL: '/sub/'.

The preview lists every template under app/emails/ and marks templates without fixture data. It shows rendered HTML, exact HTML source, or plain text. The visible page polls once per second, so saved template and fixture changes appear without a restart. Polling pauses in a hidden tab.

When Nuxt DevTools is available, its Nuxt Email iframe shortcut opens the same baseURL-aware preview. The shortcut adds no second preview application or route.

The preview calls the same server-only registry and renderEmail() implementation your handlers use. It is a rendering and debugging tool — not a second rendering path and not an email-client compatibility guarantee.

Add a fixture

Place one exact sibling file beside a template. Only the .fixtures.ts suffix is recognized, and it must default-export one props object:

app/emails/welcome.fixtures.ts
import type { EmailComponentProps } from '@lupinum/nuxt-email'
import type WelcomeEmail from './welcome.vue'

export default {
  firstName: 'Ada',
  activationUrl: 'https://example.com/activate',
} satisfies EmailComponentProps<typeof WelcomeEmail>

The preview intentionally supports one fixed scenario per template and does not accept request-provided props or generate editing forms.

Inspect output

Preview

Loads the raw rendered HTML in a sandboxed iframe (no script permission).

HTML

Displays the exact html returned by renderEmail().

Plain text

Displays the exact text returned by renderEmail().

Copy / Open

Copy copies the active representation; Open opens the raw development render in a separate tab.

Render failures show the template name, the EmailRenderError stack, and its original cause. Use the error reference to find the recovery step by symptom.

A template without a fixture remains in the list. Add its sibling fixture before trying to render it.

Mind the approximate Gmail clipping budget

Gmail is commonly observed clipping messages around 102 KB, but behavior can vary with the account, message, and delivery path. Nuxt Email reports the rendered HTML's exact UTF-8 byte count; the Gmail-specific interpretation is an approximate authoring warning, not a guarantee:

ts
const { html } = await renderEmail('welcome', props)
console.log('bytes:', Buffer.byteLength(html, 'utf8'))

Keep the rendered HTML comfortably below the warning area and test the delivered message in your target Gmail accounts. Inlined Tailwind utilities and repeated markup add up quickly, so measure your largest, most dynamic template.

The preview has no dark-mode simulation. Email clients transform dark-mode content differently, so test dark rendering in the real clients you support.

Production boundary

The preview page, endpoints, and fixture imports exist only in development. Production builds contain none of their routes or fixture data. The raw render endpoint uses a restrictive content security policy. Its iframe has no script permission.

The development endpoints cannot become production render or send APIs. Use the typed server-only renderEmail in your handlers.

Next, render the template from Nitro.