_emailmd_

Wrappers

Customize the outer email structure with custom wrappers.

Most emails work great with the default wrapper: a gray outer background with a centered white content area. Custom wrappers are for when you need full control over the outer MJML structure, such as an edge-to-edge background color, custom <mj-body> attributes, or a multi-section layout.

Custom Wrapper

Supply a WrapperFn to replace the default outer structure. Most custom wrappers only change the body, so build it with segmentsToMjml and hand it to buildDocument, which adds the <mjml> root, the head and the <mj-body> exactly as the default wrapper does:

import { render, buildDocument, segmentsToMjml } from 'emailmd';
import type { WrapperFn } from 'emailmd';

const myWrapper: WrapperFn = (segments, theme, meta) => {
  const banner = '<mj-section><mj-column><mj-text>Sent from the Acme newsletter</mj-text></mj-column></mj-section>';
  const body = segmentsToMjml(segments, theme, meta);
  return buildDocument(banner + body, theme, meta);
};

await render(md, { wrapper: myWrapper });

Pass meta through to both helpers. It carries the preheader, the dark-mode palette, the custom css, the document's lang and dir, localized strings, and the warnings collector. A wrapper that drops it, or picks out only some of its fields, silently loses those features.

The wrapper function receives three arguments:

  • segments: the parsed content blocks from the markdown (text, buttons, images, directives, tables, etc.). Each segment has a type, content, and optional attrs.
  • theme: the fully resolved theme object after merging defaults with any overrides from the render options and frontmatter.
  • meta: the render context (WrapperMeta): the preheader, the full frontmatter map including custom keys, darkTheme, lang, dir, css, strings and warnings.

Custom frontmatter keys are handy for wrapper-driven layouts:

const myWrapper: WrapperFn = (segments, theme, meta) => {
  const banner = meta?.frontmatter?.campaign === 'summer-2026'
    ? '<mj-section><mj-column><mj-text>☀️ Summer Sale</mj-text></mj-column></mj-section>'
    : '';
  return buildDocument(banner + segmentsToMjml(segments, theme, meta), theme, meta);
};

Helper Functions

emailmd exports three helpers for use inside custom wrappers:

  • segmentsToMjml(segments, theme, ctx?) converts the parsed segments into MJML body elements (sections, columns, text, buttons, images, tables, etc.). Pass the wrapper's meta as ctx so localized strings, text direction and content validation warnings carry through.
  • buildDocument(body, theme, meta?) wraps body content in the full document: <mjml> with lang and dir, the head, and the themed <mj-body>.
  • buildHead(theme, preheader?, darkTheme?, dir?, css?) generates only the <mj-head>: global styles, font imports, dark-mode overrides, custom CSS and the hidden preheader text. Use it when you need your own <mjml> root or <mj-body> attributes, and pass every argument through from meta:
const head = buildHead(theme, meta?.preheader, meta?.darkTheme, meta?.dir, meta?.css);

You can also skip the helpers entirely and write raw MJML from scratch.

On this page