Module behavior
What the module registers, its fixed conventions, and the opt-in codeBlock configuration.
Nuxt Email is a standard Nuxt module registered in modules. Discovery, preview routes, and the render API are fixed by convention. Syntax-highlighted blocks are the one opt-in feature.
export default defineNuxtConfig({
modules: ['@lupinum/nuxt-email'],
})What the module registers
- Components. Eighteen built-in
E*components are auto-imported for use inside email templates. ConfiguringcodeBlockaddsECodeBlock. renderEmail— a Nitro-only auto-import generated from the discovered templates, typed with each template's name and props.defineEmailentry point — import it from@lupinum/nuxt-email/define-emailinside a template to declare subject or authored text metadata.- Development preview — the
/__emailapplication and its endpoints, registered only whennuxt.options.devis true. - Nuxt DevTools shortcut — a development-only iframe tab pointing to that same baseURL-aware preview.
Package entry points
The published package exposes six entry points. Runtime and type-only exports are listed separately.
| Import | Runtime exports | Type-only exports |
|---|---|---|
@lupinum/nuxt-email | Default Nuxt module | ModuleOptions, RenderedEmail, EmailComponentProps |
@lupinum/nuxt-email/build | buildEmailRegistry (Node build only) | BuildEmailRegistryOptions |
@lupinum/nuxt-email/render | renderEmailComponent, EmailRenderError, UnknownEmailTemplateError | RenderedEmail, EmailComponentProps, EmailComponents |
@lupinum/nuxt-email/define-email | defineEmail, DefineEmailOutsideRenderError, DuplicateEmailDefinitionError | DefineEmailOptions |
@lupinum/nuxt-email/testing | renderEmailComponent, EmailRenderError | RenderedEmail |
@lupinum/nuxt-email/errors | EmailRenderError, UnknownEmailTemplateError, DefineEmailOutsideRenderError, DuplicateEmailDefinitionError, TailwindMissingHeadError | — |
After nuxt prepare, the generated #nuxt-email/testing alias supplies the configured testing renderer. Use it when a template depends on generated component configuration such as ECodeBlock. It follows the same renderEmailComponent and EmailRenderError testing surface.
There is no public renderEmail package export; its type is generated from each application's templates.
For a server registry without Nitro, see Render outside Nitro.
Template discovery
- Templates are
.vuefiles underapp/emails/. - Nested paths become slash-separated names (
app/emails/account/reset-password.vue→account/reset-password). app/emails/components/is reserved for your own supporting components and is not discovered.- Discovery follows the active Nuxt
srcDir: a customsrcDir: 'src/'usessrc/emails/. - Email templates from inherited Nuxt layers are not merged into the active application's registry. Keep the application-owned templates in its active
srcDir/emailstree. - In a monorepo, register the module in each Nuxt application that sends email. Each application owns one registry from its own active
srcDir/emails; package and layer templates are not merged implicitly. - Sibling
.fixtures.tsfiles supply deterministic preview props in development and are excluded from production output.
The registry is regenerated when templates are added, renamed, or deleted, which changes the generated server types. Run nuxt prepare after such changes.
Optional syntax highlighting
codeBlock is the only module option. It uses the Shiki syntax highlighter. The option requires one theme and a non-empty, closed language allowlist:
| Option | Type | Default | Required |
|---|---|---|---|
codeBlock | { languages: readonly string[], theme: string } | — | No |
codeBlock.languages | Non-empty readonly string[] | — | Yes when codeBlock is set |
codeBlock.theme | string | — | Yes when codeBlock is set |
export default defineNuxtConfig({
modules: ['@lupinum/nuxt-email'],
nuxtEmail: {
codeBlock: {
languages: ['typescript', 'vue'],
theme: 'github-dark',
},
},
})Only the configured languages and the grammars they import enter the server bundle. Nuxt Email creates the highlighter on first use. Without this option, ECodeBlock is unregistered and Shiki performs no module-setup or production-bundle work. Shiki remains an installation-time dependency. Unsupported top-level options fail during setup.
Production boundary
The production server keeps templates referenced by its registry because Nitro renders them there. The client bundle excludes those templates.
The /__email application, its endpoints, and every discovered .fixtures.ts module are absent from production output. See the preview workflow and security boundary.