---
title: "Rendering & export, MailKaro Docs"
description: "A design is JSON. To send it, render it to email-safe HTML on your server with email-craft-sdk, then hand it to any ESP."
---

# Rendering & export

Rendering

# Rendering & export

A design is JSON. To send it, render it to email-safe HTML on your server with `email-craft-sdk`, then hand it to any ESP.

ts render.ts

```
import { renderDesign, migrateDesign } from "email-craft-sdk";

app.post("/emails/:id/render", async (req, res) => {
 const stored = await db.emails.get(req.params.id); // { design }
 const design = migrateDesign(stored.design); // upgrade old schemas safely

 const html = renderDesign(design, {
 fullDocument: true, // complete <html> email
 mergeTags: { first_name: req.query.name ?? "" } // resolve tokens for this recipient
 });

 res.json({ html }); // send via Postmark / SES / SendGrid / …
});
```

## What you can produce

- **HTML**, `renderDesign(design, { fullDocument: true, mergeTags })`, email-safe across clients.
- **Design JSON**, export/import the raw design (Pro: `editor.export_json`).
- **AMP for Email**, `renderAmp(design, { mergeTags })`, an interactive AMP4EMAIL document (see below).
- **Document (print/PDF)**, `renderDocument(design, { pageSize })`, a paged, letter/A4-width sheet the browser can "Save as PDF" (see the page/document builder below).
- **ZIP bundle**, `renderEmailZip(design)` packs `index.html`, MJML, AMP, plain text, and the design JSON into one downloadable `.zip` (see below).
- **MJML / plain text / image / popup overlay**, additional outputs from the renderer and Pro export features.

## The Export menu & custom exporters

In the editor, the toolbar **Export** button downloads any format, HTML, MJML, AMP, plain text, design JSON, or a ZIP bundle. Register **your own** formats with `config.exporters`: each exporter turns the design into a file (text or binary), and appears in the menu after the built-ins. Use it to add a PDF service, a CMS payload, or an ESP-specific format without forking the editor.

ts exporters.ts

```
import { resolveExporters, builtinExporters } from "email-craft-sdk";

const config = {
 exporters: [
 {
 id: "pdf", label: "PDF", extension: "pdf", mimeType: "application/pdf", binary: true, run: async (design) => {
 const html = renderDesign(design, { fullDocument: true });
 return await myPdfService(html); // returns a Uint8Array
 }
 }
 ]
};
// Headless: resolveExporters(config.exporters) → [...builtins, pdf]
```

ℹ A custom exporter whose `id` matches a built-in (`html`, `zip`, …) replaces it, so you can swap in a smarter HTML/PDF pipeline while keeping the same menu.

## ZIP bundle

`renderEmailZip(design)` returns the raw bytes of a valid `.zip` containing `index.html`, `email.mjml`, `email.amp.html`, `email.txt`, `design.json`, and a README. The encoder is dependency-free (works in Node and the browser). In the editor it's the **ZIP bundle** item in the Export menu; via the ref it's `editorRef.current?.editor.exportZip(({ zip }) => …)`.

## Page & document builder

Set `config.builder` to `"email"` (default, ~600px), `"page"` (wider ~1024px landing page), or `"document"` (letter-width ~880px with padding). It widens the canvas to match what you're building. For document output, `renderDocument(design, { pageSize: "letter" | "a4" })` wraps the same blocks in a paged sheet with an `@page` rule and print styles, print it to PDF from the browser.

tsx document.tsx

```
<EmailEditor config={{ builder: "document" }} />

// Server / headless:
import { renderDocument } from "email-craft-sdk";
const html = renderDocument(design, { pageSize: "a4" });
```

✦ Always run `migrateDesign()` before rendering a stored design, it safely upgrades older schema versions so old saved emails keep rendering.

## AMP for Email

`renderAmp` produces an **AMP4EMAIL** document, the interactive MIME part you send *alongside* the HTML fallback. Images become `` and the [carousel](https://mailkaro.com/docs/elements) block becomes a swipeable ``; the AMP boilerplate, runtime, and any component scripts are included, and all CSS moves into ``.

ts amp.ts

```
import { renderAmp } from "email-craft-sdk";

const amp = renderAmp(design, { mergeTags: { first_name: "Sam" } });
// Send as the text/x-amp-html part, with renderDesign(...) as the text/html fallback.

// In the editor: the toolbar "Copy AMP" button, or the ref, editorRef.current?.editor.exportAmp(({ amp }) => sendAmpPart(amp));
```

ℹ AMP4EMAIL is stricter than HTML (no `!important`, no `` in the body, explicit image dimensions). MailKaro emits a structurally-valid document and stacks slides as the HTML fallback; validate the output with `amphtml-validator` for a given design before you rely on it in production. Gmail also requires senders to register for AMP email.

## Preview vs. send

The editor's `exportHtml` is a convenient preview. For production sends, render server-side with real [merge-tag](https://mailkaro.com/docs/merge-tags) values so personalization is accurate.

Source: https://docs.mailkaro.com/rendering
