Migrating from MJML

Coming from MJML, once you switch your mental model to the Vue syntax you'll find a lot of familiar concepts in Maizzle, but with more flexibility and power under the hood.

Component map

MJMLMaizzle
<mjml>Html
<mj-head>Head
<mj-title>useHead()
<mj-preview>Preheader
<mj-body>Body
<mj-section>Section
<mj-group>Row
<mj-column>Column
<mj-text>Text
<mj-button>Button
<mj-image>Img
<mj-divider>Hr
<mj-spacer>Spacer
<mj-table>Plain <table> with Tailwind classes
<mj-raw>Raw
<mj-html-attributes> defaultshtml.attributes.add
<mj-style><style> block in <Head>
<mj-include>Custom Vue component

Maizzle components

These have no MJML equivalent, here's what you've been missing out on:

ComponentPurpose
LayoutOpinionated skeleton with email-safe defaults
ContainerCentered content wrapper
HeadingHeadings with semantic markup
LinkUnstyled hyperlink
TailwindEnables Tailwind CSS for styling
MarkdownRender markdown content
CodeBlock / CodeInlineSyntax-highlighted code / Inline code formatting
FontUse custom fonts
OutlookRender content only for Outlook
NotOutlookRender content everywhere except Outlook
OutlookBgOutlook background images with VML
NoWidowsPrevent orphaned words
WithUrlAdd base URLs and tracking query params
PlaintextRender content only in the plaintext output
NotPlaintextRender content everywhere except in the plaintext output
QrCodeGenerate inline QR codes using tables

Side-by-side

<mjml>
  <mj-head>
    <mj-title>Welcome to Acme</mj-title>
    <mj-preview>Let's get you set up.</mj-preview>
    <mj-attributes>
      <mj-all font-family="Helvetica, Arial, sans-serif" />
      <mj-text color="#475569" font-size="16px" />
    </mj-attributes>
  </mj-head>
  <mj-body background-color="#ffffff">
    <mj-section>
      <mj-column>
        <mj-text font-size="24px" color="#0f172a">Hi {{ name }}</mj-text>
        <mj-text>Thanks for signing up. Click below to verify your email.</mj-text>
        <mj-button href="https://example.com/verify" background-color="#2563eb">
          Verify email
        </mj-button>
      </mj-column>
    </mj-section>
  </mj-body>
</mjml>
<script setup>
  defineProps({ name: String })

  useHead({ title: 'Welcome to Acme' })
</script>

<template>
  <Html>
    <Head />
    <Preheader>Let's get you set up.</Preheader>
    <Tailwind>
      <Body class="text-gray-600 text-base bg-white font-sans">
        <Container class="max-w-lg">
          <Text class="text-2xl text-gray-950">Hi {{ name }}</Text>
          <Text>Thanks for signing up. Click below to verify your email.</Text>
          <Button href="https://example.com/verify" class="bg-blue-600 text-white">
            Verify email
          </Button>
        </Container>
      </Body>
    </Tailwind>
  </Html>
</template>

Styling

MJML uses tag attributes (background-color, font-size, padding) on every element.

In Maizzle, you can use Tailwind utility classes or inline CSS.

Includes and partials

<mj-include path="./header.mjml" /> becomes a custom Vue component:

<template>
  <Section class="py-4">
    <Img src="/logo.png" alt="Acme" width="120" />
  </Section>
</template>
<template>
  <Html>
    <!-- ... -->
    <Body>
      <Header />      <!-- ... -->
    </Body>
  </Html>
</template>

Compiling emails

Use the Maizzle CLI in place of the MJML one:

mjml file.mjml -o dist/file.html
npx maizzle build

Or programmatically with build():

build.ts
import { build } from '@maizzle/framework'

const { files } = await build({
  content: ['emails/**/*.vue'],
  output: { path: 'dist' },
})

Or, to render a single email template and get the compiled HTML string:

import mjml2html from 'mjml'

const { html } = mjml2html('<mjml>...</mjml>')
import { render } from '@maizzle/framework'

// Render from file path (relative to project root)
const { html } = await render('emails/welcome.vue')

// Or from a SFC string
const { html } = await render('<template>...</template>')