Maizzle vs MJML:どちらのフレームワークでメールを作るべきか

Mara Levin 著 · 2026年10月7日更新 · ドキュメントと価格は同日に確認

どちらも無料で、読みやすいソースからメールクライアント向けの HTML を生成します。違いは、新しいタグの語彙を覚えるか、Vue コンポーネントと Tailwind のクラスを書くかという点です。

結論から

チームがすでに Vue と Tailwind を書いていて、テンプレートの中で条件分岐や繰り返しを使いたく、入力中に未対応の CSS を指摘してくれる開発サーバーが欲しいなら Maizzle です。メールをコンパイルするのが JavaScript の世界にいない人やサービスである場合、セクションとカラムという少数の部品で組みたい場合、すでに使っているツールが MJML を受け付ける場合(Loops がそうです)は MJML が向いています。どちらも利用料は無料なので、ライセンス費用でこの選択が決まることはありません。

ひと目でわかる比較表

観点 Maizzle MJML
書くものTailwind CSS 4 でスタイルを付ける Vue 単一ファイルコンポーネントXML 風のタグ:mj-section、mj-column、mj-text
プロジェクトの開始npx maizzle new のあと npx maizzle servenpm install mjml、またはオンラインエディタ
コンパイルnpx maizzle build、または Node から build() / render()mjml file.mjml -o dist/file.html、または Node から mjml2html
テンプレート内のロジックVue の v-if、v-for、<script setup>なし。テンプレートタグは mj-raw に入れ、後段で処理する
組み込みコンポーネント30以上(Button から QR コードまで)標準セットとコミュニティ製コンポーネント
Node 以外での利用Node でのビルドのみ他言語向けのポートとバインディングあり
利用コスト料金プランは公開されていないMJML の FAQ によれば MITライセンス

同じウェルカムメールを両方で書く

Maizzle のドキュメントには、MJML から移る人向けの移行ページがあり、同じメールが両方の書き方で載っています。以下の2つのコードはそのページから引用したものです(MJML ユーザー向け Maizzle ガイド)。同じページにコンポーネントの対応表もあり、2 つの語彙がどれだけ離れているかを知るには手っ取り早い資料です。

MJML

<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>

Maizzle

<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>

目につく違いは 3 つあります。1 つ目は、スタイルの指定が各タグの属性から Tailwind のクラスに移ること。2 つ目は、ページの head が mj-title と mj-preview ではなく、useHead() と Preheader コンポーネントで組み立てられること。3 つ目は {{ name }} の意味で、MJML では後から埋めるプレースホルダーですが、Maizzle では Vue の prop です。そのため同じファイルに任意の名前を渡して描画でき、条件分岐、繰り返し、算出値といった Vue の道具をそのまま使えます。

タグの対応を一覧にする

上の 2 つのコードと、Maizzle の移行ガイドで挙げられている対応から、よく使う部品を並べ直しました。移行の見積もりをするときの目安になります。

MJML Maizzle 書き方の変化
mj-titleuseHead({ title })タグから関数呼び出しへ
mj-previewPreheaderタグ名の置き換え
mj-attributesTailwind のクラス共通指定をクラスに移す
mj-sectionSectionタグ名の置き換え
mj-columnColumnタグ名の置き換え
mj-textText色やサイズは属性からクラスへ
mj-buttonButtonbackground-color 属性が bg-blue-600 などのクラスへ
mj-include独自の Vue コンポーネントファイルの取り込みからコンポーネントの配置へ
mj-raw のテンプレート構文v-if、v-for後段の処理から、ビルド中のロジックへ

この表で「タグ名の置き換え」にあたる行は、機械的に直せる部分です。手間がかかるのは、各行の最後の 3 つ、つまり属性から Tailwind のクラスへの書き換え、mj-include のコンポーネント化、mj-raw のロジックの移し替えです。テンプレートを移す前に、自分の資産でこの 3 種類がどれだけ使われているかを数えれば、作業量が見えてきます。

日々の作業で感じる違い

動的コンテンツ

MJML 自体には変数の仕組みがありません。MJML のドキュメントは、テンプレート構文を mj-raw の中に入れるよう案内しています。また、そのタグの中の < のような文字は minify で壊れることがあると警告しており、回避策は htmlmin:ignore コメントです。実際には、先に MJML をコンパイルし、できた HTML に対して Handlebars、Liquid、自前のコードなどを走らせる流れになります。Maizzle は Vue コンポーネントをサーバーサイドレンダリングで描画するので、ロジックはビルド中に実行され、そのビルドからプレーンテキスト版も得られます。

共通パーツの扱い

MJML には mj-include がありますが、導入前に知っておきたい落とし穴があります。インクルードは、Node では ignoreIncludes: false、コマンドラインでは --config.allowIncludes true を指定して有効にしない限り、何も言わずに無視されます。出力からヘッダーが消えたら、まずここを疑ってください。Maizzle で同じことをするには Vue コンポーネントを使います。移行ガイドでは、小さな Header コンポーネントを body の中に置く例が示されています。

import mjml2html from 'mjml'

async function compile(source) {
  const { html, errors } = await mjml2html(source, { ignoreIncludes: false })
  return html
}

これは MJML のドキュメントにある、非同期の mjml2html 呼び出しと ignoreIncludes オプションに沿った書き方です。インクルードに相対パスを使う場合、同じドキュメントのページでは filePath も渡して、MJML が解決の起点を分かるようにしています。

開発サーバーとチェック機能

Maizzle の開発サーバーには、ライブプレビュー、デバイスサイズの切り替え、ダークモードのエミュレーション、Checks タブがあります。チェックは 2 種類で、caniemail.com のデータをもとにした互換性の警告と、href のない Button や alt のない画像を指摘する lint ルールです。既定の対象は Gmail、Apple Mail、Outlook、Yahoo! です。MJML のツールはもう少し軽く、CLI の watch モード、検証レベルの設定(strict、soft、skip)、エディタのプラグインが中心です。どちらも実際のメールクライアントのスクリーンショットの代わりにはならず、Maizzle の互換性ページ自身もそう書いています。

Outlook などクセの強いクライアント

どちらのフレームワークも、テーブルや条件付きコメントといった定番の回避策を内部で包んでいて、手で書く必要はありません。Maizzle の Button には、従来型 Outlook 向けの余白を指定する msoPt、msoPb、msoPx といった prop と、outlookFallback のスイッチがあります。そのクライアントを直接狙いたい場面のために、Outlook、NotOutlook、OutlookBg(VML の背景画像)というコンポーネントも用意されています。MJML のドキュメントは、ある制限をはっきり書いています。mj-button は、サポートが限られているため一部のクライアントでボタン全体がクリックできるとは限らない、というものです。どちらの方式でも構いませんが、実際の Outlook のプレビューでボタンの四隅をクリックして確かめてください。

日本語メールのフォント指定

どちらのフレームワークを選んでも、日本語メールのフォントの並び順と行間は自分で決めます。MJML では、上のサンプルにもある mj-attributes の mj-all に font-family を書けば、全部品に共通の指定になります。Maizzle のサンプルでは、本文のスタイルを Body の Tailwind クラス(font-sans など)で与えているので、日本語のフォントを並べるときも、ここを起点にします。並べる順序と行間の目安は メールの CSS の記事にまとめています。次の例は、MJML 側の書き方の一つです。

<mj-attributes>
  <mj-all font-family="'Hiragino Kaku Gothic ProN', 'Hiragino Sans', 'Yu Gothic', Meiryo, sans-serif" />
  <mj-text font-size="16px" line-height="1.8" />
</mj-attributes>

この指定を入れたら、Outlook for Windows を含む実際のクライアントで、行間と折り返しが想定どおりかを確かめてください。フォントが端末にない場合にどの代替が使われるかは、並べた順で決まります。

プロジェクトの置き場所

Maizzle は独立したリポジトリにも、アプリの中の Vite プラグインにもできます。ドキュメントには Laravel、Nuxt、SvelteKit、Astro が挙げられており、プラグインは自分のアプリの隣で Maizzle 専用の開発サーバーを動かし、vite build の後にメールをコンパイルすると説明されています。MJML はビルドの外にあります。CLI や mjml2html を好きな場所から呼ぶだけなので、フロントエンドの道具立てがない Rails、Django、.NET の現場にもそのまま入ります。

HTML を取り出して送る

コンパイルして終わりではありません。Maizzle は Nodemailer、SendGrid、Mailgun、Postmark、Resend、AWS SES、Cloudflare 向けのデプロイ手順を公開しています。そのうち Resend 向けのデプロイのページの例では、テンプレートを描画して数行で送信できます。

import { Resend } from 'resend'
import { render } from '@maizzle/framework'

const resend = new Resend(process.env.RESEND_API_KEY)
const { html, plaintext } = await render('emails/welcome.vue')

await resend.emails.send({
  from: 'hello@yourdomain.com',
  to: 'user@example.com',
  subject: 'Welcome',
  html,
  text: plaintext,
})

事前にビルドしておく場合は、プログラムから呼ぶ次の形になります。

import { build } from '@maizzle/framework'

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

無料フレームワーク以外にかかる費用

フレームワーク自体は安い部分です。請求額に効いてくるのはプレビューと送信の2点です。Email on Acid の料金ページでは、Basic が月額 $99 でプレビュー1,000件、Premium が月額 $199 で2,000件、超過分は1件あたり $0.15 とされています。クライアントやデバイスごとのスクリーンショット1枚が1プレビューに数えられるため、50クライアントを対象にすれば1回のテストで50件を消費します。つまり Basic で月に約20回、Premium で約40回の全クライアント確認ができる計算です。送信前に1通を5回作り直せば、この枠はすぐ減ります。年間の予算に直すと、Basic は $99 × 12 = $1,188、Premium は $199 × 12 = $2,388 で、フレームワークの利用料が0であるぶん、この金額が実質のツール費用の中心になります。そのため、確認の回数を減らせるように、入力中に未対応の CSS を指摘してくれる開発サーバーを備えたフレームワークを選ぶ意味があります。

送信側では、Resend の料金ページに、Free プランが月 $0(月3,000通、1日100通)、Pro が $20(50,000通)と載っています。どちらのフレームワークの出力も使え、Maizzle のドキュメントには手順がそのまま示されています。他の配信サービスも比べたい場合は、当サイトの Resend vs Postmark と Postmark vs SendGrid に数字を並べています。

Resend の料金ページ(2026年10月に取得したスクリーンショット)
Resend の料金ページ(2026年10月7日時点)。出典:https://resend.com/pricing

決める前に試す:同じメールを1通ずつ作る手順

記事を読み比べるより、半日かけて同じメールを両方で作れば、どちらがチームに合うかがはっきりします。上の表にあるコマンドだけで、次の手順で進められます。

  1. 題材を 1 つ決めます。ここまで例に使ってきた、メールアドレス確認のウェルカムメール(見出し、本文、ボタン 1 つ)が手ごろです。
  2. MJML 側では npm install mjml を実行し、welcome.mjml に上のサンプルを貼って、mjml welcome.mjml -o dist/welcome.html でコンパイルします。ここまでに要る時間は数分です。
  3. Maizzle 側では npx maizzle new でプロジェクトを作り、npx maizzle serve で開発サーバーを立ち上げ、emails/welcome.vue に Maizzle のサンプルを貼ります。Checks タブに出る警告を確認します。
  4. どちらも出力された HTML を、同じプレビューサービスの同じクライアントに通します。Outlook でボタンの四隅をクリックし、ダークモードでの背景色も見ます。
  5. 最後に、名前の差し込みを「ありがとう、{{ name }} さん」のように条件分岐付きの文に変えてみます。Maizzle では v-if で書けます。MJML では mj-raw にテンプレート構文を入れて、別のツールで処理する形になります。この手間の差が、日々の運用で効いてきます。

見るべきなのは、完成した HTML の美しさではありません。書いている最中のストレス、レビューのしやすさ、CI に載せたときの手間の 3 点です。1 人で書くのか、複数人で直すのかによっても、感じ方は変わります。

チームの構成から考える

  • フロントエンド中心の小さな SaaS チーム: すでに Vue と Tailwind があるなら、Maizzle のコンポーネントとクラス名がそのまま使えます。メールのためだけに別の語彙を覚える必要がなく、デザイントークンも共有できます。
  • バックエンドが Ruby、Python、PHP、.NET などの組織: Node を新しく足すこと自体が負担になる場合があります。MJML は、CI で 1 度コンパイルして HTML を成果物として渡す形にできるため、既存の構成を崩しません。
  • マーケティング担当がテンプレートを触る組織: mj-section と mj-column だけで組み立てる MJML のほうが、読んで直せる人が多くなります。一方、Tailwind のクラス名に慣れた担当者がいれば、Maizzle でも問題ありません。
  • メールの種類が多く、国や言語でバリエーションが出る組織: 繰り返しや条件分岐をテンプレート内に書ける Maizzle が、重複の少ない構成にしやすい作りです。

どちらにしても、ライセンス費用は発生しないため、判断の材料は、人の学習コスト、レビューのしやすさ、CI との相性の 3 つです。プレビューサービスと配信サービスの費用は、どちらを選んでも変わりません。

つまずきやすい点の早見表

ここまでに出てきた落とし穴を、症状から引けるようにまとめます。いずれも前の節で出典のページを挙げたものです。

症状 考えられる原因 対処
MJML の出力からヘッダーが消えたmj-include が既定では無視されるNode は ignoreIncludes: false、CLI は --config.allowIncludes true
mj-raw の中の < が壊れるminify が文字を処理してしまうhtmlmin:ignore コメントで囲む
相対パスのインクルードが見つからないMJML が解決の起点を知らないfilePath を渡す
ネットの Maizzle 記事のコードが動かない旧バージョン(テーブル + クラス)向けの内容6.x のドキュメントと照らし合わせる
Outlook でボタンの余白が崩れる従来型 Outlook 用の余白指定がないmsoPt、msoPb、msoPx、outlookFallback を使う
MJML のボタンの端がクリックできない一部クライアントでのサポートの限界実際の Outlook のプレビューで四隅を確認する
Node のないサーバーでビルドしたいMaizzle は Node でのビルドが前提CI で MJML をコンパイルして HTML を渡す

Maizzle を選ぶ場合

  • デザイナーと開発者がすでに Tailwind の設定を共有していて、メールもそれに合わせたい。
  • テンプレートに本物のロジックが必要で、言語の切り替え、データからの行の繰り返し、任意表示のブロックなどがある。
  • プレビューサービスを開く前に、フレームワーク側で未対応の CSS を警告してほしい。
  • メールが Vite アプリ(Laravel、Nuxt、SvelteKit、Astro)の隣にあり、コンポーネントを共有したい。

MJML を選ぶ場合

  • バックエンドが JavaScript ではない、または複数のサービスが同じソースをコンパイルする。
  • レイアウトを mj-section と mj-column で済ませる短い語彙を好み、Tailwind のことは考えたくない。
  • 使っているプラットフォームが MJML を直接受け付ける。たとえば Loops は、独自のエディタの代わりに自分の MJML をアップロードできる。
  • フロントエンド開発者以外もテンプレートを読んでレビューする。

片方からもう片方へ移るとき

MJML から Maizzle への移行は、Maizzle が対応表を用意しているので、こちらの方向のほうが資料に恵まれています。まずパスワードリセットのような影響の小さいメールを 1 通作り直し、両方の版を同じクライアントのプレビューに通して、スクリーンショットを見比べてください。逆方向、つまり Maizzle から MJML へは、Vue のロジックをプレースホルダーに平たく直す作業になるため、条件付きブロックを後からどこに置くかを先に決めておく必要があります。他の組み合わせは MJML vs React Email と React Email vs Maizzle を、背景にある CSS の制約は メールにおける CSS と クライアント互換性ガイド を参照してください。

よくある質問

Maizzle は今でも「HTML のテーブルに Tailwind を足したもの」ですか?

現在のドキュメントを読む限り、そうではありません。Maizzle は Vue と Tailwind CSS で HTML メールを作るフレームワークと自己紹介しており、テンプレートは Vue の単一ファイルコンポーネント、ドキュメントのバージョンは 6.x です。手書きのテーブルにユーティリティクラスを付ける解説記事は以前のワークフローの話なので、コードをコピーする前にそのチュートリアルがどのバージョン向けかを確認してください。

既存の MJML テンプレート資産を Maizzle に移せますか?

ワンクリックの変換ツールはありませんが、Maizzle は対応表を公開しています。mj-section は Section、mj-column は Column、mj-button は Button、mj-preview は Preheader に置き換わり、mj-include は独自の Vue コンポーネントになります。独自属性の少ないメールから移し、残りを移す前にメールクライアントのプレビューを見比べてください。

Node を使わないチームにはどちらが導入しやすいですか?

MJML です。公式ドキュメントには他の言語向けのポートやバインディングが載っており、CI で一度コンパイルしてできた HTML を、どの言語で書かれたバックエンドにも渡せます。Maizzle は Vue を使う Node ビルドが前提で、単独のプロジェクトとしても、既存アプリの Vite プラグインとしても動かします。

どちらも実際のメールクライアントでテストしてくれますか?

いいえ。Maizzle は開発サーバーで caniemail.com のデータをもとに互換性チェックと lint を行い、送信前には実際のクライアントで表示テストをするよう案内しています。MJML は独自のルールでマークアップを検証するだけです。クライアントごとのスクリーンショットが必要なら、Email on Acid や Litmus のようなプレビューサービスが別に要ります。