Getting Started

Migrating from Vue Email

Step-by-step guide to migrate your existing Vue Email project to Nuxt Email Renderer.

Learn how to migrate your existing Vue Email project to Nuxt Email Renderer. This guide covers all the steps needed to transition smoothly while maintaining your existing email templates and functionality.

Why Migrate?

Unfortunately, Vue Email is no longer actively maintained as of September, 2025.

In an ideal world, we should have Vue Email working & maintained, so that it can provide a great experience for Vue & Nuxt users. However, the reality is that the project has been abandoned by its maintainers, and there are no signs of it being picked up again.

Unfortunately, I don't have the resources to maintain two separate libraries that serve a similar purpose. Therefore, I have decided to focus all my efforts on Nuxt Email Renderer, which I can actively maintain.

Nuxt Email Renderer offers several advantages over Vue Email:

  • Native Nuxt 4+ Integration - Built specifically for Nuxt applications
  • Better DevTools Support - Visual email template preview and debugging
  • Simplified API - Server-side rendering endpoints built-in
  • TypeScript First - Full TypeScript support out of the box
  • Hot Module Replacement - Instant template updates during development
This module does not provide the ETailwind component available in Vue Email. Currently, it is also not part of the roadmap for future development because it requires significant additional code and maintenance.

Migration Steps

Step 1: Remove Vue Email Dependencies

Remove the existing Vue Email packages from your project:

npm uninstall @vue-email/components @vue-email/render

Step 2: Install Nuxt Email Renderer

Install the Nuxt Email Renderer module:

npm install nuxt-email-renderer

Add the module to your nuxt.config.ts:

nuxt.config.ts
export default defineNuxtConfig({
  modules: ["nuxt-email-renderer"],
});

For detailed installation instructions, see the Installation Guide.

Step 3: Remove Rollup Config (if applicable)

Remove Rollup config from Nitro settings if you have it configured:

nuxt.config.ts
export default defineNuxtConfig({
 nitro: {
-    rollupConfig: {
-      plugins: [vue()], // see https://vuemail.net/getting-started/nuxt-nitro
-    },
  },
})

Step 4: Move Email Templates

Move all your email templates to an emails directory. For Nuxt 4+ projects, use the app/emails directory. For older projects or if you prefer, use the root emails directory:

Before (Vue Email):

your-project/
├── src/
│   └── emails/
│       ├── WelcomeEmail.vue
│       └── ResetPasswordEmail.vue
└── package.json

After (Nuxt Email Renderer with Nuxt 4):

your-project/
├── app/
│   └── emails/       # 📧 Nuxt 4 structure
│       ├── WelcomeEmail.vue
│       └── ResetPasswordEmail.vue
├── nuxt.config.ts
└── package.json

Or (Nuxt Email Renderer with root directory):

your-project/
├── emails/           # 📧 Root directory
│   ├── WelcomeEmail.vue
│   └── ResetPasswordEmail.vue
├── nuxt.config.ts
└── package.json
The module automatically detects app/emails (Nuxt 4 structure) first, then falls back to emails (root directory) for backward compatibility.

If you prefer to keep your templates in a different location, configure the emails directory:

nuxt.config.ts
export default defineNuxtConfig({
  modules: ["nuxt-email-renderer"],

  nuxtEmailRenderer: {
    emailsDir: "/src/emails", // Custom directory
  },
});

Step 5: Update Template Imports

Update your email templates to use Nuxt Email Renderer components:

Before (Vue Email):

emails/WelcomeEmail.vue
<template>
  <Html>
    <Head />
    <Body>
      <Container>
        <Heading>Welcome {{ userName }}!</Heading>
        <Text>Thank you for joining us.</Text>
        <Button :href="confirmUrl">Confirm Account</Button>
      </Container>
    </Body>
  </Html>
</template>

<script setup>
import { Html, Head, Body, Container, Heading, Text, Button } from '@vue-email/components'

interface Props {
  userName: string
  confirmUrl: string
}

defineProps<Props>()
</script>

After (Nuxt Email Renderer):

emails/WelcomeEmail.vue
<template>
  <EHtml>
    <EHead />
    <EBody>
      <EContainer>
        <EHeading>Welcome {{ userName }}!</EHeading>
        <EText>Thank you for joining us.</EText>
        <EButton :href="confirmUrl">Confirm Account</EButton>
      </EContainer>
    </EBody>
  </EHtml>
</template>

<script setup lang="ts">
// No imports needed - components are auto-imported!

interface Props {
  userName: string;
  confirmUrl: string;
}

defineProps<Props>();
</script>

Component Mapping

Here's how Vue Email components map to Nuxt Email Renderer components:

Vue EmailNuxt Email RendererNotes
<Html><EHtml>Same functionality
<Head><EHead>Same functionality
<Body><EBody>Same functionality
<Container><EContainer>Same functionality
<Text><EText>Same functionality
<Heading><EHeading>Same functionality
<Button><EButton>Same functionality
<Img><EImg>Same functionality
<Link><ELink>Same functionality
<Hr><EHr>Same functionality
<Section><ESection>Same functionality
<Row><ERow>Same functionality
<Column><EColumn>Same functionality
<CodeBlock><ECodeBlock>Same functionality
<CodeInline><ECodeInline>Same functionality
<Font><EFont>Same functionality
<Preview><EPreview>Same functionality
<Style><EStyle>Same functionality
<EMarkdown><EMarkdown>Same functionality
<ETailwind>Not implemented

Step 6: Update Rendering Logic

Replace your Vue Email rendering code with Nuxt Email Renderer API calls:

Before (Vue Email):

import { render } from "@vue-email/render";
import WelcomeEmail from "./emails/WelcomeEmail.vue";

const html = await render(WelcomeEmail, {
  userName: "John Doe",
  confirmUrl: "https://example.com/confirm",
});

After (Nuxt Email Renderer):

server/api/send-email.post.ts
export default defineEventHandler(async (event) => {
  const html = await renderEmailComponent("WelcomeEmail", {
    userName: "John Doe",
    confirmUrl: "https://example.com/confirm",
  });

  // Send the email using your email service
  // await sendEmail({ to: 'user@example.com', html })

  return { success: true };
});
Nuxt Email Renderer uses server-side rendering with the renderEmailComponent function. This approach is more secure than exposing API endpoints publicly, as it keeps your email templates private and prevents abuse.

Verification

After migration, verify everything works:

  1. Start your development server:
    npm run dev
    
  2. Check Nuxt DevTools:
    • Open your browser's DevTools
    • Navigate to the "Nuxt Email Renderer" tab
    • Verify your templates are listed
  3. Test rendering in your server code:
    server/api/test-email.get.ts
    export default defineEventHandler(async () => {
      const html = await renderEmailComponent("WelcomeEmail", {
        userName: "Test User",
        confirmUrl: "https://example.com/confirm",
      });
    
      return html;
    });
    

    Then visit http://localhost:3000/api/test-email to see the rendered HTML.

Common Migration Issues

Template Not Found

Problem: Template 'WelcomeEmail' not found

Solution:

  • Ensure templates are in the correct directory (/emails by default)
  • Check filename matches exactly (case-sensitive)
  • Verify the template file extension is .vue

Component Import Errors

Problem: Components not recognized in templates

Solution:

  • Remove manual component imports from Vue Email
  • Components are auto-imported in Nuxt Email Renderer
  • Ensure you're using the E prefix (e.g., EHtml, EBody)

Props Type Errors

Problem: TypeScript errors with template props

Solution:

  • Ensure defineProps<Props>() is used correctly
  • Add lang="ts" to your script setup block
  • Verify interface definitions match your API calls

Need Help?

If you encounter issues during migration: