Deutsch
Migration von 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. @templatical/import-mjml konvertiert ein MJML-Dokument direkt in Templaticals TemplateContent-Format — installieren Sie es, führen Sie es aus, und nutzen Sie die folgenden Abschnitte, um alles nachzuarbeiten, was es nicht automatisch abbilden kann.
Installation
bash
npm install @templatical/import-mjmlbash
pnpm add @templatical/import-mjmlbash
yarn add @templatical/import-mjmlbash
bun add @templatical/import-mjmlOhne Build-Schritt (CDN)
Sie können es auch von einem CDN laden:
html
<script type="module">
import { convertMjmlTemplate } from 'https://cdn.jsdelivr.net/npm/@templatical/import-mjml/+esm';
// ...dann konvertieren wie im Abschnitt „Verwendung" unten
</script>Verwendung
ts
import { convertMjmlTemplate } from '@templatical/import-mjml';
// Den rohen MJML-Quelltext einer E-Mail laden
const res = await fetch('/path/to/email.mjml');
const mjml = await res.text();
// In das Templatical-Format konvertieren
const { content, report } = convertMjmlTemplate(mjml);
// Im Editor verwenden
const editor = await init({
container: '#editor',
content,
});
// Konvertierungsbericht auf Auffälligkeiten prüfen
console.log(report);convertMjmlTemplate arbeitet synchron und gibt ein ImportResult zurück mit:
content— das konvertierteTemplateContent, bereit für den Editorreport— ein Konvertierungsbericht mit dem Status jedes Quellelements (converted,approximated,html-fallbackoderskipped)
Den Bericht lesen
Jeder Eintrag in report.entries beschreibt ein Quellelement:
| Status | Bedeutung |
|---|---|
converted | Jedes Attribut mit einer Templatical-Entsprechung wurde übernommen. |
approximated | Auf den richtigen Block abgebildet, aber ein Wert musste auf einen begrenzten Wertebereich angepasst werden — note nennt den ursprünglichen Wert. |
html-fallback | Keine Block-Entsprechung vorhanden; das ursprüngliche Markup bleibt in einem HtmlBlock erhalten. |
skipped | Es wurde nichts erzeugt (templaticalBlockType: null). |
ts
console.log(report.summary);
// { total: 24, converted: 21, approximated: 2, htmlFallback: 1, skipped: 0 }
for (const entry of report.entries) {
if (entry.status === 'approximated') {
console.warn(`<${entry.sourceTag}> approximiert:`, entry.note);
}
}
// <mj-section> approximiert: Column widths 40%, 60% have no exact Templatical layout; resolved to "1-2".Ein note bei einem approximated-Eintrag nennt immer den ersetzten Wert — ein Diff von report.entries zwischen zwei Durchläufen zeigt so genau, was eine Migration verändert hat.
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, parsen Sie das MJML und bauen einen äquivalenten JSON-Baum auf — einschließlich der Auflösung von MJMLs eigenen Attribut-Vererbungsregeln (mj-all, Tag-Defaults, mj-class), bevor jedes Element abgebildet wird. Genau das leistet @templatical/import-mjml; die Mapping-Tabelle unten zeigt, was es umsetzt.
Pfad 1 — Visuell mit dem MJML als Referenz neu aufbauen
Bei einer Handvoll Templates ist der Neuaufbau von Hand neben Ihrer MJML-Quelle oft schneller, als ein Paket zu installieren:
- Ö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. Bei größeren Mengen führen Sie zuerst @templatical/import-mjml aus und nutzen diesen Pfad nur, um nachzuarbeiten, was als HTML-Fallback-Block gelandet ist.
Pfad 2 — Templaticals Renderer zur Verifikation nutzen
Sobald ein Template in Templatical vorliegt — importiert oder von Hand nachgebaut:
ts
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.
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-group | SectionBlock.stackOnMobile: false | Kein eigener Block — markiert, dass die Spalten der Section auf Mobilgeräten nebeneinander bleiben, statt zu stapeln. |
mj-text | TitleBlock / TableBlock / MenuBlock / ParagraphBlock | Strukturell aufgelöst: Eine einzelne Überschrift als Wurzelelement wird zu TitleBlock, eine einzelne <table> zu TableBlock, durch <span> getrennte Top-Level-Anker ohne Paragraph-Wrapper und ohne Nicht-Whitespace-Textknoten als Geschwister zu MenuBlock, ein Satz Fließtext plus ein abschließendes <a> zu ParagraphBlock, alles andere zu ParagraphBlock. |
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. Die Plattform kommt aus name, dem src-Dateinamen (Pack-Suffixe wie -round-outlined werden abgetrennt) oder alt. |
mj-navbar (mit mj-navbar-link) | MenuBlock | Jeder Link → MenuItemData. |
mj-table | TableBlock | Bildet <tr>/<td>/<th>-Zeilen und -Zellen auf Templaticals Tabellen-Daten ab; eine führende <th>-Zeile setzt hasHeaderRow. |
mj-raw | HtmlBlock | Inneres Markup bleibt wortgetreu erhalten. |
mj-wrapper | SectionBlock.wrapper | Das äußere Band der Section, keine eigene Section. Eine einzelne Section darin fließt in deren wrapper; mehrere teilen sich dasselbe Band und werden als approximated markiert. |
mj-hero, mj-carousel, mj-accordion | HtmlBlock | Wird in einen HTML-Block mit erhaltenem Original-Markup konvertiert. |
mj-head-Inhalte | Template-settings | mj-preview → preheaderText; mj-attributes/mj-font/mj-style setzen Schriftart, Textfarbe und Link-Farbe/-Unterstreichung des Dokuments. mj-title hat keine Entsprechung in den Settings und wird mit einer Warnung verworfen. |
Wo das Mapping verlustbehaftet ist
MJML, das Templaticals eigener Renderer erzeugt hat, durchläuft den Importer ohne Näherungen bei Layout, Styling oder Anzeigebedingungen. Die einzige Lücke betrifft den Blocktyp: Ein VideoBlock und ein HtmlBlock rendern beide als reines MJML ohne Kennzeichnung ihrer Herkunft, sodass beim erneuten Import jeweils ein anderer Blocktyp entsteht (siehe unten). Handgeschriebenes MJML konvertiert für jedes Tag in der Mapping-Tabelle oben sauber; alles, was die Tabelle nicht abdeckt, landet als HtmlBlock mit dem ursprünglichen Markup. Innerhalb dessen, was die Tabelle abdeckt, sind einige Konvertierungen Näherungen statt exakter Treffer:
- Spalten-Geometrie — Templatical unterstützt fünf Spalten-Layouts (
1,2,3,2-1,1-2). MJML erlaubt beliebig viele Spalten in beliebigem Verhältnis, daher wird ein Verhältnis außerhalb dieser fünf auf das nächstliegende Layout aufgelöst, und der Inhalt einer vierten oder weiteren Spalte fließt in die letzte Spalte. - Social-Icon-Größen —
SocialIconsBlockunterstützt drei Größen (24px, 32px, 48px). Eineicon-sizean einemmj-social-elementaußerhalb dieser drei wird auf die nächstliegende aufgelöst. - Überschriften-Ebenen — ein
<h5>oder<h6>innerhalb vonmj-textwird auf Überschriften-Ebene 4 begrenzt, die höchste, die einTitleBlockunterstützt. - Video-Blöcke — ein
VideoBlockwird genauso gerendert wie ein verlinkterImageBlock, sodass nichts im MJML ihn als Video kennzeichnet. Beim Import daraus entsteht einImageBlockmit demselben Vorschaubild und Link; der Inhalt bleibt erhalten, der Blocktyp nicht. - HTML-Blöcke — aus demselben Grund rendert der Inhalt eines
HtmlBlockals reinesmj-text-Markup ohne jede Kennzeichnung als HTML. Beim Import daraus entsteht einParagraphBlockmit demselben Markup. - Block-IDs — jeder importierte Block erhält eine neu generierte ID. IDs erscheinen nirgends im gerenderten MJML, daher überlebt nichts, das an einer ID hängt — zum Beispiel ein Cloud-Kommentarthread — einen Durchlauf durch Export und Re-Import.
Was sich nicht automatisch überträgt
mj-include— der Importer liest einen einzelnen MJML-String ohne Dateisystemzugriff, daher wird ein nicht auflösbares<mj-include>übersprungen (skipped), mit einer Warnung, die daspath-Attribut nennt. Inkludierten Inhalt vor dem Import inlinen.- Custom MJML-Components — ein nicht erkanntes
mj-*-Tag landet automatisch alsHtmlBlockmit dem gerenderten Markup. Implementieren Sie es als Templatical Custom Block, wenn Sie es stattdessen als nativen Block editierbar haben möchten.
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 die Abdeckung von @templatical/import-mjml zu verbessern.