Migration von handgeschriebenem MJML
Diese Anleitung richtet sich an Teams, die E-Mail-Templates bisher in rohem MJML erstellt haben (mit Editoren wie VS Code, einem internen CLI oder einer selbstgebauten Build-Pipeline) und auf Templaticals visuellen Editor wechseln möchten.
Automatischer Importer in Entwicklung
Wir entwickeln einen automatischen @templatical/import-mjml, der ein MJML-Dokument parst und einen Templatical-TemplateContent-Baum erzeugt. Aktiv in Entwicklung. Bis er ausgeliefert ist, dokumentiert diese Seite den manuellen Weg.
MJML → Templatical ist schwerer vollständig zu automatisieren als BeeFree → Templatical, weil MJML eine echte Obermenge dessen ist, was Templaticals JSON-Baum ausdrücken kann (es lässt sich gültiges MJML schreiben, das keinen Templatical-Block-Equivalent hat). Der Importer wird die häufigen Muster abdecken und für alles außerhalb des Mappings auf HtmlBlock zurückfallen.
Was hier eigentlich passiert
Diese Migration ist etwas kontraintuitiv. Templaticals Renderer erzeugt MJML als Ausgabe — auf den ersten Blick sehen MJML und Templatical identisch aus. Aber:
- MJML ist eine Markup-Sprache. Sie schreiben XML-ähnliche Tags (
<mj-section>,<mj-column>,<mj-text>) und der MJML-Compiler verwandelt das in tabellenbasiertes HTML. - Templatical speichert Templates als JSON-Baum mit typisierten Blöcken (
SectionBlock,ParagraphBlockusw.) und rendert diesen Baum beim Export zu MJML.
Um ein MJML-Template in Templatical zu bringen, müssen Sie das MJML parsen und einen äquivalenten JSON-Baum aufbauen. Dafür gibt es noch kein integriertes Werkzeug — siehe "Automatischer Importer in Entwicklung" oben.
Pfad 1 — Visuell mit dem MJML als Referenz neu aufbauen (empfohlen)
Haben Sie weniger als 20 MJML-Templates, ist das mit Abstand der schnellste Weg:
- Öffnen Sie Ihre MJML-Quelle im Editor Ihrer Wahl.
- Öffnen Sie den Templatical-Editor (oder den Playground) daneben.
- Kompilieren Sie Ihr MJML einmal zu HTML und sehen Sie es sich an — das ist Ihr visuelles Ziel.
- Ziehen Sie die entsprechenden Templatical-Blöcke hinein (siehe Mapping-Tabelle unten).
- Kopieren Sie Textinhalte direkt. Bilder über Ihre Medienbibliothek neu hosten.
- Bilden Sie Styling über Templaticals Design-Tokens ab, statt über inline
mj-attributes.
Die meisten MJML-Templates sind in 10–20 Minuten umgezogen, sobald Sie eines oder zwei gemacht haben.
Pfad 2 — Templaticals Renderer zur Verifikation nutzen
Sobald Sie ein Template visuell nachgebaut haben:
import { renderToMjml } from '@templatical/renderer';
const mjml = await renderToMjml(content);
// Vergleichen Sie dieses MJML mit Ihrem ursprünglichen MJML-Quelltext.Ein Diff zwischen Original und dem von Templatical erzeugten MJML zeigt strukturelle Unterschiede. Eine sinnvolle Sanity-Prüfung vor einer Bulk-Migration.
Pfad 3 — Ein einmaliges Konvertierungs-Skript schreiben
Haben Sie Hunderte MJML-Templates und wollen automatische Konvertierung versuchen, bevor der offizielle Importer da ist, ist der praktische Ansatz, einen kleinen XML-/HTML-Parser zu nutzen (htmlparser2, node-html-parser), den MJML-Baum zu durchwandern und für jedes Tag Templaticals Block-Factories aufzurufen.
Hier die grobe Form:
import { parse } from 'node-html-parser';
import {
createSectionBlock,
createTitleBlock,
createParagraphBlock,
createImageBlock,
createButtonBlock,
} from '@templatical/types';
import type { TemplateContent, Block } from '@templatical/types';
function mjmlToTemplate(mjml: string): TemplateContent {
const root = parse(mjml);
const body = root.querySelector('mj-body');
const blocks: Block[] = (body?.childNodes ?? [])
.map((node) => convertNode(node))
.filter((b): b is Block => b !== null);
return {
blocks,
settings: {
width: parseInt(body?.getAttribute('width') ?? '600'),
backgroundColor: body?.getAttribute('background-color') ?? '#ffffff',
},
};
}
function convertNode(node: any): Block | null {
switch (node.tagName?.toLowerCase()) {
case 'mj-section':
return convertSection(node);
case 'mj-text':
return convertText(node);
// …weitere Cases — siehe Mapping-Tabelle unten
default:
return null; // oder Fallback auf HtmlBlock
}
}WARNING
Ein selbst geschriebener Parser wird Edge Cases übersehen — verschachtelte mj-wrapper, Custom Components, bedingte Tags, Includes (mj-include) und Attribut-Vererbung über mj-attributes. Lassen Sie die Konvertierung zuerst auf einer kleinen Stichprobe laufen und vergleichen Sie visuell, bevor Sie im Bulk konvertieren.
MJML-Tag-Mapping
| MJML-Tag | Templatical-Block | Hinweise |
|---|---|---|
mj-section (mit mj-columns) | SectionBlock mit columns | Mehrspaltige Layouts funktionieren gleich; Spaltenbreiten kommen aus MJMLs width-Attribut oder werden gleichmäßig verteilt. |
mj-column | Section-Spalte | Eine Spalte hält eine Liste verschachtelter Blöcke. |
mj-text | ParagraphBlock (oder TitleBlock bei einer Überschrift) | Anhand inline-styled Heading-Level entscheiden, ob Title oder Paragraph. |
mj-image | ImageBlock | src, alt, href, width, Padding. |
mj-button | ButtonBlock | href, background-color, color, Schrift, Padding. |
mj-divider | DividerBlock | border-color, border-width, Padding. |
mj-spacer | SpacerBlock | height. |
mj-social (mit mj-social-element) | SocialIconsBlock | Jedes mj-social-element → ein SocialIcon-Eintrag. |
mj-navbar (mit mj-navbar-link) | MenuBlock | Jeder Link → MenuItemData. |
mj-table | TableBlock | <tr>- und <td>-Zeilen/Zellen auf Templaticals Tabellen-Daten abbilden. |
mj-raw | HtmlBlock | HTML-Pass-Through. |
mj-wrapper | SectionBlock (oft) | Ein Wrapper ohne Spalten wird zu einer Section mit einer Spalte. |
mj-hero, mj-carousel, mj-accordion | HtmlBlock (Fallback) | Templatical hat noch keine direkten Entsprechungen — das gerenderte HTML in einen rohen HTML-Block übernehmen oder auf den Importer warten. |
mj-head-Inhalte | Template-settings | mj-title, mj-preview, mj-attributes, mj-font, mj-style mappen auf TemplateSettings.preheaderText, eigene Schriften und Theme-Overrides. |
Was sich nicht automatisch übertragen lässt
mj-attributes-Defaults — MJML erlaubt globale Defaults für jedes Tag. Übertragen Sie diese in Templaticals Block-Defaults und Theme-Overrides.mj-include— MJMLs Include-Direktive hat keine direkte Entsprechung. Inkludierten Inhalt während der Konvertierung inlinen.- Custom MJML-Components — wenn Sie eigene MJML-Komponenten registriert haben, müssen Sie sie entweder (a) als Templatical Custom Blocks implementieren oder (b) auf
HtmlBlockmit dem gerenderten HTML zurückfallen. - Bedingte MSO-Tags innerhalb von
mj-raw— bewahren Sie sie, indem Sie das ursprüngliche Markup in einenHtmlBlockpacken.
Wenn diese Anleitung etwas nicht abdeckt
Eröffnen Sie eine Diskussion mit einem geschwärzten Ausschnitt Ihres MJMLs und was Sie erreichen wollen. Wir nutzen diese Rückmeldungen, um zu priorisieren, welche MJML-Muster der automatische Importer zuerst abdeckt.