Maizzle ou MJML : quel framework pour construire vos e-mails ?
Par Mara Levin · Mis à jour le 7 octobre 2026 · documentation et prix vérifiés ce jour-là
Les deux sont gratuits et transforment un code source lisible en HTML compatible avec les clients de messagerie. L’un vous demande d’apprendre un nouveau vocabulaire de balises, l’autre d’écrire des composants Vue et des classes Tailwind.
La réponse courte
Choisissez Maizzle si votre équipe écrit déjà du Vue et du Tailwind, veut de la logique conditionnelle et des boucles dans les gabarits, et apprécierait un serveur de développement qui signale le CSS non pris en charge pendant la frappe. Choisissez MJML si les e-mails sont compilés par des personnes ou des services qui ne vivent pas dans l’écosystème JavaScript, si vous voulez un petit vocabulaire de sections et de colonnes prêtes à l’emploi, ou si un outil que vous utilisez déjà accepte le MJML (c’est le cas de Loops). Le prix de licence ne tranche pas ici, puisqu’aucun des deux n’en demande.
En un coup d’œil
| Question | Maizzle | MJML |
|---|---|---|
| Ce que vous écrivez | Des composants monofichiers Vue stylés avec Tailwind CSS 4 | Des balises de type XML : mj-section, mj-column, mj-text |
| Démarrer un projet | npx maizzle new, puis npx maizzle serve | npm install mjml, ou l’éditeur en ligne |
| Compilation | npx maizzle build, ou build() / render() depuis Node | mjml file.mjml -o dist/file.html, ou mjml2html dans Node |
| Logique dans le gabarit | Vue : v-if, v-for, <script setup> | Aucune ; les balises de gabarit vont dans mj-raw et s’exécutent à une étape ultérieure |
| Composants intégrés | Plus de 30, du bouton aux codes QR | Le jeu standard, plus des composants communautaires |
| Fonctionne hors de Node | Build Node uniquement | Portages et bindings pour d’autres langages |
| Coût d’utilisation | Aucun tarif de plan publié | Licence MIT, d’après la FAQ de MJML |
Le même e-mail de bienvenue dans les deux
La documentation de Maizzle comporte une page de migration pour les utilisateurs venant de MJML, avec le même e-mail écrit des deux façons. Les deux blocs ci-dessous en sont tirés, avec le texte visible de l’e-mail traduit en français : voir le guide de Maizzle pour les utilisateurs de MJML. Il fournit aussi une table de correspondance des composants, ce qui permet de mesurer rapidement l’écart réel entre les deux vocabulaires.
MJML
<mjml>
<mj-head>
<mj-title>Bienvenue chez Acme</mj-title>
<mj-preview>Configurons votre compte.</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">Bonjour {{ name }}</mj-text>
<mj-text>Merci pour votre inscription. Cliquez ci-dessous pour confirmer votre adresse e-mail.</mj-text>
<mj-button href="https://example.com/verify" background-color="#2563eb">
Confirmer l’adresse e-mail
</mj-button>
</mj-column>
</mj-section>
</mj-body>
</mjml> Maizzle
<script setup>
defineProps({ name: String })
useHead({ title: 'Bienvenue chez Acme' })
</script>
<template>
<Html>
<Head />
<Preheader>Configurons votre compte.</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">Bonjour {{ name }}</Text>
<Text>Merci pour votre inscription. Cliquez ci-dessous pour confirmer votre adresse e-mail.</Text>
<Button href="https://example.com/verify" class="bg-blue-600 text-white">
Confirmer l’adresse e-mail
</Button>
</Container>
</Body>
</Tailwind>
</Html>
</template> Trois points ressortent. Le style passe d’attributs posés sur chaque balise à des classes Tailwind. L’en-tête de la page s’assemble avec useHead() et un composant Preheader à la place de mj-title et mj-preview. Enfin, {{ name }} n’est plus un espace réservé que l’on remplit plus tard : dans Maizzle, c’est une prop Vue, de sorte que le même fichier peut produire un rendu pour n’importe quel prénom transmis, avec toute la boîte à outils de Vue (conditions, boucles, valeurs calculées) autour.
Ce qui change au quotidien
Contenu dynamique
MJML n’a pas de variables propres. La documentation de MJML demande de placer la syntaxe du moteur de gabarits dans mj-raw et prévient que la minification peut buter sur des caractères comme < dans ces balises, ce que l’on contourne avec des commentaires htmlmin:ignore. En pratique, on compile d’abord, puis on passe Handlebars, Liquid ou son propre code sur le HTML obtenu. Maizzle rend ses composants Vue par rendu côté serveur : la logique s’exécute pendant le build, et celui-ci peut aussi produire une version en texte brut.
Parties partagées
MJML propose mj-include, avec un piège à connaître avant de l’adopter : les includes sont ignorés sans avertissement tant que vous ne les activez pas, avec ignoreIncludes: false dans Node ou --config.allowIncludes true en ligne de commande. Si votre en-tête disparaît du résultat, c’est la première chose à vérifier. L’équivalent côté Maizzle est un composant Vue ; le guide de migration en donne un exemple sous forme d’un petit composant Header placé dans le corps du message.
import mjml2html from 'mjml'
async function compile(source) {
const { html, errors } = await mjml2html(source, { ignoreIncludes: false })
return html
} Cet extrait suit l’appel asynchrone à mjml2html et l’option ignoreIncludes présentés dans la documentation de MJML. Lorsque vos includes utilisent des chemins relatifs, la même page de documentation transmet aussi un filePath pour que MJML sache à partir d’où résoudre ces chemins.
Serveur de développement et contrôles
Le serveur de développement de Maizzle offre un aperçu en direct, le redimensionnement selon l’appareil, l’émulation du mode sombre et un onglet Checks. Les contrôles sont de deux sortes : des avertissements de compatibilité construits à partir des données de caniemail.com, et des règles de lint, par exemple un Button sans href ou une image sans alt. Par défaut, ils ciblent Gmail, Apple Mail, Outlook et Yahoo!. L’outillage de MJML est plus léger : un mode de surveillance des fichiers (watch) en ligne de commande, un niveau de validation réglable (strict, soft ou skip) et des extensions pour éditeurs de code. Ni l’un ni l’autre ne remplace de vraies captures par client, et la page de compatibilité de Maizzle le dit elle-même.
Outlook et les autres clients difficiles
Les deux frameworks encapsulent les astuces de tableaux et de commentaires conditionnels pour que vous n’ayez pas à les écrire à la main. Le Button de Maizzle accepte des props comme msoPt, msoPb et msoPx pour la marge intérieure dans Outlook classique, ainsi qu’un interrupteur outlookFallback ; il fournit aussi les composants Outlook, NotOutlook et OutlookBg (images d’arrière-plan en VML) pour les cas où il faut viser ce client directement. La documentation de MJML est plus franche sur une limite : mj-button n’est pas cliquable sur toute sa surface dans certains clients, faute de prise en charge suffisante. Les deux approches se défendent ; l’essentiel est de cliquer sur les coins du bouton dans un vrai aperçu Outlook.
Où vit le projet
Maizzle peut former un dépôt à lui seul ou servir de plugin Vite dans une application. La documentation cite Laravel, Nuxt, SvelteKit et Astro, et décrit le plugin comme un Maizzle lancé dans son propre serveur de développement à côté du vôtre, qui compile les e-mails après vite build. MJML reste en dehors de votre build : vous appelez la ligne de commande ou mjml2html où vous voulez, ce qui explique qu’il convienne aux équipes Rails, Django ou .NET sans chaîne d’outils front-end.
Récupérer le HTML
Le HTML compilé ne fait que la moitié du travail. Maizzle documente des recettes de déploiement pour Nodemailer, SendGrid, Mailgun, Postmark, Resend, AWS SES et Cloudflare. Celle de Resend, tirée de sa page de déploiement, génère un gabarit et l’envoie en quelques lignes :
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: 'Bienvenue',
html,
text: plaintext,
}) Pour une étape de build exécutée à l’avance, la forme programmatique est la suivante :
import { build } from '@maizzle/framework'
const { files } = await build({
content: ['emails/**/*.vue'],
output: { path: 'dist' },
}) Ce que coûte la chaîne autour de deux frameworks gratuits
Le framework est la partie bon marché. Deux postes comptent dans la facture : les aperçus et l’envoi. Sur sa page tarifs, Email on Acid affiche Basic à 99 $ par mois pour 1 000 aperçus et Premium à 199 $ pour 2 000, avec des dépassements à 0,15 $ par aperçu. Chaque capture d’un client ou d’un appareil compte pour un aperçu : un test sur 50 clients en consomme donc 50. Basic couvre ainsi environ 20 passages complets par mois, Premium environ 40. Si vous reconstruisez un e-mail cinq fois avant de l’envoyer, ce budget fond vite, ce qui plaide pour le framework qui offre la boucle de retour la plus rapide, quel qu’il soit. Pour comparer les deux services d’aperçu, voyez notre page Litmus vs Email on Acid.
Côté envoi, la page tarifs de Resend indique une offre gratuite à 0 $ pour 3 000 e-mails par mois (100 par jour) et Pro à 20 $ pour 50 000. Le HTML produit par l’un ou l’autre framework fonctionne avec ce service, et la documentation de Maizzle montre comment faire. Pour comparer d’autres solutions d’envoi, nos pages Resend vs Postmark et Postmark vs SendGrid mettent les chiffres côte à côte.
Choisir Maizzle si…
- Designers et développeurs partagent déjà une configuration Tailwind et que vous voulez que l’e-mail la suive.
- Les gabarits ont besoin d’une vraie logique : changement de langue, lignes répétées à partir de données, blocs facultatifs.
- Vous voulez que le framework vous signale le CSS non pris en charge avant même d’ouvrir un service d’aperçu.
- Les e-mails vivent à côté d’une application Vite (Laravel, Nuxt, SvelteKit, Astro) et partagent des composants avec elle.
Choisir MJML si…
- Votre backend n’est pas en JavaScript, ou plusieurs services doivent compiler la même source.
- Vous préférez un vocabulaire court où la mise en page se résume à
mj-sectionetmj-column, sans vous soucier de Tailwind. - Une plateforme que vous utilisez accepte directement le MJML. Loops, par exemple, permet de téléverser votre propre MJML à la place de son éditeur.
- Des personnes autres que les développeurs front-end doivent lire et relire les gabarits.
Passer de l’un à l’autre
Le passage de MJML à Maizzle est la direction la mieux documentée, puisque Maizzle fournit la table de correspondance. Reconstruisez d’abord un e-mail à faible enjeu, comme une réinitialisation de mot de passe, envoyez les deux versions dans les mêmes aperçus clients et comparez les captures. Dans l’autre sens, il faut aplatir la logique Vue en espaces réservés : décidez dès le départ où se logeront ensuite les blocs conditionnels. Pour d’autres duels, voyez MJML vs React Email et React Email vs Maizzle ; pour les limites du CSS qui sous-tendent tout cela, lisez le guide sur le CSS dans les e-mails et celui sur la compatibilité entre clients.
Questions fréquentes
Maizzle, est-ce toujours « des tableaux HTML plus Tailwind » ?
Plus dans la documentation actuelle. Maizzle se présente comme un framework pour créer des e-mails HTML avec Vue et Tailwind CSS, les gabarits sont des composants monofichiers Vue et la documentation est versionnée en 6.x. Les anciens tutoriels qui montrent des tableaux écrits à la main avec des classes utilitaires décrivent une méthode de travail d’une version antérieure : vérifiez à quelle version un tutoriel se rapporte avant de copier son code.
Puis-je migrer une bibliothèque MJML existante vers Maizzle ?
Il n’existe pas de convertisseur en un clic, mais Maizzle publie une table de correspondance des composants. mj-section devient Section, mj-column devient Column, mj-button devient Button, mj-preview devient Preheader et mj-include devient un composant Vue personnalisé. Commencez par les e-mails qui utilisent le moins d’attributs spécifiques et comparez les aperçus clients avant de migrer le reste.
Lequel demande le moins de mise en place pour une équipe qui n’utilise pas Node ?
MJML. Sa documentation recense des portages et des bindings pour plusieurs autres langages, et vous pouvez compiler une fois en CI puis remettre le HTML à un backend écrit dans n’importe quoi. Maizzle suppose un build Node avec Vue, soit comme projet autonome, soit comme plugin Vite dans une application existante.
L’un ou l’autre teste-t-il mes e-mails dans de vrais clients de messagerie ?
Non. Maizzle exécute des contrôles de compatibilité et de lint dans son serveur de développement à partir des données de caniemail.com, et recommande de lancer des tests de rendu dans de vrais clients avant l’envoi. MJML valide votre balisage selon ses propres règles. Pour obtenir de vraies captures par client, il faut toujours un service d’aperçu comme Email on Acid ou Litmus.