Skip to content

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, ParagraphBlock usw.) 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:

  1. Öffnen Sie Ihre MJML-Quelle im Editor Ihrer Wahl.
  2. Öffnen Sie den Templatical-Editor (oder den Playground) daneben.
  3. Kompilieren Sie Ihr MJML einmal zu HTML und sehen Sie es sich an — das ist Ihr visuelles Ziel.
  4. Ziehen Sie die entsprechenden Templatical-Blöcke hinein (siehe Mapping-Tabelle unten).
  5. Kopieren Sie Textinhalte direkt. Bilder über Ihre Medienbibliothek neu hosten.
  6. 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:

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.

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:

ts
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-TagTemplatical-BlockHinweise
mj-section (mit mj-columns)SectionBlock mit columnsMehrspaltige Layouts funktionieren gleich; Spaltenbreiten kommen aus MJMLs width-Attribut oder werden gleichmäßig verteilt.
mj-columnSection-SpalteEine Spalte hält eine Liste verschachtelter Blöcke.
mj-textParagraphBlock (oder TitleBlock bei einer Überschrift)Anhand inline-styled Heading-Level entscheiden, ob Title oder Paragraph.
mj-imageImageBlocksrc, alt, href, width, Padding.
mj-buttonButtonBlockhref, background-color, color, Schrift, Padding.
mj-dividerDividerBlockborder-color, border-width, Padding.
mj-spacerSpacerBlockheight.
mj-social (mit mj-social-element)SocialIconsBlockJedes mj-social-element → ein SocialIcon-Eintrag.
mj-navbar (mit mj-navbar-link)MenuBlockJeder Link → MenuItemData.
mj-tableTableBlock<tr>- und <td>-Zeilen/Zellen auf Templaticals Tabellen-Daten abbilden.
mj-rawHtmlBlockHTML-Pass-Through.
mj-wrapperSectionBlock (oft)Ein Wrapper ohne Spalten wird zu einer Section mit einer Spalte.
mj-hero, mj-carousel, mj-accordionHtmlBlock (Fallback)Templatical hat noch keine direkten Entsprechungen — das gerenderte HTML in einen rohen HTML-Block übernehmen oder auf den Importer warten.
mj-head-InhalteTemplate-settingsmj-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 HtmlBlock mit dem gerenderten HTML zurückfallen.
  • Bedingte MSO-Tags innerhalb von mj-raw — bewahren Sie sie, indem Sie das ursprüngliche Markup in einen HtmlBlock packen.

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.