Skip to content

Renderer ​

@templatical/renderer konvertiert Templatical-Template-JSON in MJML. Funktioniert sowohl in Browser- als auch in Node.js-Umgebungen.

Der Renderer erzeugt ausschließlich MJML. Um MJML für den E-Mail-Versand zu HTML zu kompilieren, verwenden Sie eine beliebige MJML-Bibliothek (mjml für Node.js, spatie/mjml-php für PHP usw.).

bash
npm install @templatical/renderer
bash
pnpm add @templatical/renderer
bash
yarn add @templatical/renderer
bash
bun add @templatical/renderer

renderToMjml(content, options?) ​

Rendert ein TemplateContent-Objekt zu einem MJML-String. Gibt ein Promise<string> zurück – asynchron, damit benutzerdefinierte Blöcke (deren Auflösung asynchrone Arbeit erfordern kann) inline gerendert werden können.

ts
import { renderToMjml } from '@templatical/renderer';

const mjml = await renderToMjml(templateContent);

Parameter:

ParameterTypeBeschreibung
contentTemplateContentDas zu rendernde Template
optionsRenderOptionsOptionale Rendering-Konfiguration

Rückgabewert: Promise<string> -- MJML-Markup

RenderOptions ​

ts
interface RenderOptions {
  customFonts?: CustomFont[];
  defaultFallbackFont?: string;
  allowHtmlBlocks?: boolean;      // Standard: true
  customBlocks?: CustomBlockDefinition[];
  renderCustomBlock?: (block: CustomBlock) => Promise<string>;
  blockRenderers?: BlockRendererMap;
  getCustomBlockStylesheet?: (customType: string) => string | undefined | null;
  socialIconsBaseUrl?: string;
}
OptionDefaultBeschreibung
customFonts[]Definitionen benutzerdefinierter Schriftarten für <mj-font>-Deklarationen in der gerenderten Ausgabe
defaultFallbackFont'Arial, sans-serif'Fallback-Stack, der an eine benutzerdefinierte Schriftart ohne eigenen fallback oder an eine einzelne, nicht eingebaute Schriftfamilie angehängt wird. Ein vollständiger Stack wird unverändert ausgegeben.
allowHtmlBlockstrueAuf false setzen, um HTML-Blöcke aus der Ausgabe zu entfernen
customBlocks--CustomBlockDefinition[] für Headless-Rendering. Bei gleichen Typen gewinnt die letzte Definition; unbekannte Typen verwenden gespeichertes renderedHtml und renderedStylesheet. Bei vorhandenen Blöcken ist der optionale Peer liquidjs ^10.30.0 erforderlich. Siehe den Leitfaden für benutzerdefinierte Blöcke.
renderCustomBlock--Wandelt benutzerdefinierte Blöcke in HTML um. Wird einmal pro Block aufgerufen. Der Editor übergibt dies über editor.toMjml(). Nicht mit customBlocks kombinierbar. Wenn keine der beiden Optionen übergeben wird, verwendet jeder benutzerdefinierte Block renderedHtml (falls vorhanden).
blockRenderers{}Typbezogene Renderer-Overrides, keyed nach block.type. Ein Countdown-GIF-Backend nutzt dies. Ein Override übernimmt die Prüfung auf „auf allen Viewports ausgeblendet“.
getCustomBlockStylesheet--(customType) => string | undefined | null. Wird einmal pro eindeutigem customType aufgerufen. Nicht mit customBlocks kombinierbar. Eine gelistete Definition entscheidet über das Stylesheet ihres Typs, auch wenn keines ausgegeben wird; unbekannte Typen verwenden gespeichertes renderedStylesheet. Ein übergebener Resolver entscheidet für jeden Typ und ignoriert gespeicherte Kopien.
socialIconsBaseUrlversionsgebundene jsDelivr-URLBasis-URL (ohne abschließenden Schrägstrich) für die PNG-Assets der Social-Media-Icons. Wird pro Icon zu ${baseUrl}/${dir}/${platform}.png aufgelöst, wobei dir der Stil ist, bei einem iconTone von dark oder light gefolgt von -${tone}. Siehe Social-Media-Icons unten.

Benutzerdefinierte Blöcke ​

Headless-Aufrufer übergeben dieselben Definitionen, die sie im Editor registrieren:

ts
import { renderToMjml } from '@templatical/renderer';
import type { CustomBlockDefinition, TemplateContent } from '@templatical/types';

export async function renderTemplate(content: TemplateContent, definitions: CustomBlockDefinition[]) {
  return renderToMjml(content, { customBlocks: definitions });
}

Für einen eigenen Resolver verwenden Sie die gemeinsame Liquid-Factory:

ts
import * as liquidjs from 'liquidjs';
import { createCustomBlockLiquid } from '@templatical/types';
const liquid = createCustomBlockLiquid(liquidjs);

Fallback, genaue Konflikte und Fehlermeldung bei fehlendem Peer sowie die festen Optionen der Factory stehen im Leitfaden für benutzerdefinierte Blöcke. {{ body | raw }} erhält HTML aus einem dafür vorgesehenen Feld. Setzen Sie Attributwerte immer in Anführungszeichen und validieren Sie URL-Schemata aus nicht vertrauenswürdigen Quellen. Nur der Editor-Canvas bereinigt den gerenderten Block; die E-Mail-Ausgabe behält das HTML des Templates.

Social-Media-Icons ​

Social-Icon-Blöcke werden als <img src="…/{dir}/{platform}.png"> ausgegeben. dir ist bei Markenfarben der Icon-Stil (circle) und bei einem iconTone von dark oder light {style}-{tone} (circle-dark). Der Standardwert von socialIconsBaseUrl verweist auf den versionsgebundenen jsDelivr-Mirror von @templatical/renderer, der vorgerasterte PNGs (jede SocialPlatform × 6 Stile × 3 Farbtöne) mit dem Paket ausliefert:

https://cdn.jsdelivr.net/npm/@templatical/renderer@<version>/assets/social/{dir}/{platform}.png

Warum PNGs. Outlook Desktop (Word-Rendering-Engine) unterstützt kein SVG und lehnt base64-Daten-URIs in <img src> ab. Gehostete PNGs sind das einzige Format, das in allen gängigen E-Mail-Clients zuverlässig dargestellt wird.

Warum versionsgebunden. E-Mails sind Archivinhalt — Empfänger öffnen Nachrichten Monate oder Jahre nach dem Versand. Die Versionsbindung friert die Icon-Darstellung zum Renderzeitpunkt ein, sodass ein späteres Redesign oder ein Regressionsfehler im Paket bereits zugestellte E-Mails nicht rückwirkend beschädigt. Außerdem entfällt eine 302-Weiterleitung pro Icon und es können langlebige, unveränderliche Cache-Header gesetzt werden.

Selbst hosten. Überschreiben Sie socialIconsBaseUrl, um die Assets über Ihr eigenes CDN auszuliefern — nützlich für Air-Gapped-Umgebungen, markenspezifische Themen oder um die Abhängigkeit von jsDelivr zu entfernen:

ts
const mjml = await renderToMjml(content, {
  socialIconsBaseUrl: 'https://cdn.example.com/email-assets/social',
});

Die exakten Dateinamen, die der Renderer erwartet, sind {dir}/{platform}.png, wobei dir ein style aus solid | outlined | rounded | square | circle | plain ist, für diese Farben gefolgt von -dark oder -light (ein Icon in Markenfarbe hat kein Suffix), und platform einer von facebook | twitter | instagram | linkedin | youtube | tiktok | pinterest | email | whatsapp | telegram | discord | snapchat | reddit | github | dribbble | behance | website ist. Die ausgelieferten 144×144-PNGs sind ein sinnvoller Ausgangspunkt, wenn Sie sie spiegeln möchten.

Das Paket exportiert außerdem DEFAULT_SOCIAL_ICONS_BASE_URL, falls Sie URLs gegen denselben Standardwert komponieren möchten:

ts
import { DEFAULT_SOCIAL_ICONS_BASE_URL } from '@templatical/renderer';

Hilfsfunktionen ​

Der Renderer exportiert außerdem Hilfsfunktionen:

ts
import {
  escapeHtml,
  escapeAttr,
  convertMergeTagsToValues,
  isHiddenOnAll,
  toPaddingString,
  renderBlock,
  getCssClassAttr,
  getCssClasses,
  getWidthPercentages,
  getWidthPixels,
  fluidOnMobileAttr,
  DEFAULT_SOCIAL_ICONS_BASE_URL,
  RenderContext,
} from '@templatical/renderer';

escapeHtml(text) ​

Maskiert HTML-Entitäten (<, >, &, ", ') in einem String. Verwenden Sie dies, wenn Sie Benutzerinhalte in HTML einfügen:

ts
escapeHtml('<script>alert("xss")</script>');
// '&lt;script&gt;alert(&quot;xss&quot;)&lt;/script&gt;'

escapeAttr(text) ​

Maskiert einen String für die sichere Verwendung in HTML-Attributwerten:

ts
const alt = escapeAttr('Photo of "sunrise" at O\'Hare');
// 'Photo of &quot;sunrise&quot; at O&#x27;Hare'

convertMergeTagsToValues(html) ​

Konvertiert HTML-Spans für Merge-Tags (vom Rich-Text-System des Editors intern verwendet) zurück in ihre einfache Textsyntax. Der Editor speichert Merge-Tags als <span data-merge-tag="...">-Elemente; diese Funktion entfernt die Spans und lässt die rohe Merge-Tag-Syntax übrig:

ts
import { convertMergeTagsToValues } from '@templatical/renderer';

// Eingabe: internes HTML-Format des Editors
const editorHtml = '<span data-merge-tag="{{ first_name }}">First Name</span>';

const cleaned = convertMergeTagsToValues(editorHtml);
// Ausgabe: '{{ first_name }}'

TIP

Sie müssen diese Funktion normalerweise nicht direkt aufrufen — der Renderer ruft sie intern beim Verarbeiten von Title- und Paragraph-Blöcken auf. Sie wird für fortgeschrittene Anwendungsfälle exportiert, in denen Sie mit Editor-HTML außerhalb der normalen Rendering-Pipeline arbeiten.

isHiddenOnAll(block) ​

Gibt true zurück, wenn bei der visibility eines Blocks alle Viewports auf false gesetzt sind. Nützlich, um Blöcke zu überspringen, die überhaupt nicht gerendert werden sollen:

ts
if (isHiddenOnAll(block)) {
  // Diesen Block vollständig überspringen
}

toPaddingString(padding) ​

Konvertiert einen SpacingValue in einen CSS-Padding-String:

ts
toPaddingString({ top: 10, right: 20, bottom: 10, left: 20 });
// '10px 20px 10px 20px'

renderBlock(block, context) ​

Rendert einen einzelnen Block in seine MJML-Darstellung. Wird intern von renderToMjml() verwendet, aber für fortgeschrittene Anwendungsfälle exportiert, in denen Sie einzelne Blöcke rendern müssen.

RenderContext ​

Das an Block-Renderer übergebene Kontextobjekt. Enthält Rendering-Optionen, Schriftart-Konfiguration und zwei Breiten:

FeldWert
containerWidthDer Anteil der Spalte an der Body-Breite, ohne Padding und Rahmen um sie herum. Ein Bild oder Video mit 'full' wird in dieser Breite ausgegeben, und MJML begrenzt es auf seine tatsächliche Box.
contentWidthDie ganzen Pixel, die MJML dem Inhalt der Spalte gibt: die Body-Breite abzüglich des seitlichen Paddings und Rahmens von Wrapper-Band und Sektion, davon der Prozentanteil der Spalte, abgerundet. Breitenabhängige Entscheidungen lesen diesen Wert.

In einer Hero-Sektion hat der Inhalt keine Spalte: containerWidth ist die Box des Heros (die Body-Breite abzüglich der Bänder um ihn herum) und contentWidth diese Box abzüglich des seitlichen Paddings des Heros. Ein Hero zeichnet keinen Rahmen, daher wird keiner abgezogen.

withContainerWidth(width, contentWidth = width) gibt eine Kopie für einen schmaleren Container zurück.

fluidOnMobileAttr(block, context) ​

Gibt ' fluid-on-mobile="true"' für ein Bild zurück, das seine Spalte auf dem Desktop füllt, sonst ''. Ein Bild füllt seine Spalte, wenn seine width 'full' ist oder eine Pixelbreite, die context.contentWidth abzüglich des seitlichen Paddings und Rahmens des Blocks erreicht oder überschreitet. Siehe Breite auf Smartphones.

Die eingebauten Bild- und Video-Renderer verwenden sie. Eine blockRenderers-Überschreibung, die ein eigenes mj-image ausgibt, ruft sie ebenfalls auf; ohne das Attribut behält das Bild auf dem Smartphone seine Desktop-Breite:

ts
blockRenderers: {
  image: (block, ctx) => {
    const image = block as ImageBlock;
    const width = image.width === 'full' ? ctx.containerWidth : image.width;
    return `<mj-image src="${escapeAttr(image.src)}" width="${width}px"${fluidOnMobileAttr(image, ctx)} />`;
  },
},

getCssClassAttr(block) / getCssClasses(block) ​

Erzeugen CSS-Klassen-Attribute aus den Sichtbarkeitseinstellungen eines Blocks. Wird intern für responsives Ausblenden verwendet.

getWidthPercentages(layout) / getWidthPixels(layout, containerWidth) ​

Berechnen Spaltenbreiten für ein gegebenes ColumnLayout. Gibt ein Array von Prozent- oder Pixelwerten pro Spalte zurück.

DEFAULT_SOCIAL_ICONS_BASE_URL ​

Der Standardwert von RenderOptions.socialIconsBaseUrl — die versionsgebundene jsDelivr-URL, die auf die in diesem Paket mitgelieferten PNGs der Social-Media-Icons verweist. Siehe Social-Media-Icons.

MJML zu HTML kompilieren ​

Nach dem Rendern in MJML kompilieren Sie mit einer beliebigen MJML-Bibliothek zu HTML:

ts
import { renderToMjml } from '@templatical/renderer';
import mjml2html from 'mjml';

const mjml = await renderToMjml(templateContent);
const { html } = mjml2html(mjml);

// html ist bereit zum Versand über Ihren E-Mail-Dienst

Weitere Informationen zur Rendering-Pipeline finden Sie unter Wie das Rendering funktioniert.