unlayer-export
Flagged{"isSafe":false,"isSuspicious":true,"riskLevel":"medium","findings":[{"category":"other","severity":"medium","description":"Credential leakage risk: The Cloud API usage example shows constructing an Authorization header from an API key in client-side code (Authorization: 'Basic ' + Buffer.from('YOUR_API_KEY:').toString('base64')). If this snippet is used in frontend, the key could be exposed. Recommend moving such calls server-side or using restricted, short-lived credentials/tokens and not embedding secrets in client code.","evidence":"Authorization: 'Basic ' + Buffer.from('YOUR_API_KEY:').toString('base64')"},{"category":"other","severity":"medium","description":"Potential XSS/HTML injection risk: Exported HTML and design JSON can include user-provided HTML. Rendering this content on a page without proper sanitization/escaping could introduce XSS. Recommend sanitizing/safely escaping HTML on output and validating/sanitizing content before rendering.","evidence":"html: data.html and design JSON may contain user-provided HTML content (see Export HTML flow)"}],"summary":"The content is largely about how to export and save Unlayer designs. No malicious commands or payloads are present. However, two risk areas exist: (1) API keys shown in client-side examples could be exposed if used in frontend code, and (2) exporting HTML/design content could lead to XSS if the HTML is rendered without proper sanitization. Mitigations include avoiding embedding secrets in frontend code, using server-side API calls or restricted tokens, and sanitizing HTML before rendering."}
npx machina-cli add skill unlayer/unlayer-skills/unlayer-export --openclawExport Content
Overview
Unlayer supports multiple export formats. Some are client-side (free), others use the Cloud API (paid).
Which Export Method?
| Method | Output | Paid? | Use When |
|---|---|---|---|
exportHtml | HTML + design JSON | No | Email sending, web publishing, saving designs |
exportPlainText | Plain text + design | No | SMS, accessibility fallback |
exportImage | PNG URL + design | Yes | Thumbnails, previews, social sharing |
exportPdf | PDF URL + design | Yes | Print-ready documents |
exportZip | ZIP URL + design | Yes | Offline download packages |
Critical: Always save the design JSON alongside any export. All export methods return
data.design— save it so users can edit later.
Save & Load Designs
// SAVE — use exportHtml to get both design JSON and HTML
unlayer.exportHtml(async (data) => {
await fetch('/api/templates', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
design: data.design, // Save this — needed to edit later
html: data.html, // The rendered HTML output
}),
});
});
// LOAD — restore a saved design (must wait for editor:ready)
unlayer.addEventListener('editor:ready', async () => {
const response = await fetch('/api/templates/123');
const saved = await response.json();
unlayer.loadDesign(saved.design); // Pass the saved JSON object
});
// LOAD BLANK
unlayer.loadBlank({ backgroundColor: '#ffffff', contentWidth: '600px' });
// LOAD AN UNLAYER TEMPLATE
unlayer.loadTemplate(templateId); // ID from Unlayer dashboard
Export HTML
unlayer.exportHtml((data) => {
const { html, design, chunks } = data;
// html — Full HTML document (string)
// design — Design JSON (always save this!)
// chunks — { css, js, body, fonts, tags }
// body — Just the content inside <body> (no wrapper)
// css — Extracted CSS styles
// fonts — Web fonts used in the design
// Save both to your backend
await fetch('/api/templates', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ design, html }),
});
}, {
// All options are optional
cleanup: true, // Remove editor markup (default: true)
minify: false, // Minify HTML output
inlineStyles: false, // Move CSS inline (for email clients)
mergeTags: {}, // Replace merge tags with real values
title: 'My Email', // Set HTML <title>
});
Using chunks — when you need just the body content (no <!DOCTYPE> wrapper):
unlayer.exportHtml((data) => {
const { body, css, fonts } = data.chunks;
const myHtml = `<style>${css}</style>${fonts}${body}`;
});
Export Plain Text
unlayer.exportPlainText((data) => {
const { text, design } = data;
// Use as email plain-text fallback
}, {
ignorePreheader: false,
ignoreLinks: false,
ignoreImages: false,
mergeTags: {},
});
Export Image (Paid — Cloud API)
Generates a PNG screenshot of the design. The image uploads to your connected File Storage.
Client-side:
unlayer.exportImage((data) => {
// data.url — PNG URL
// data.design — Design JSON (always save this!)
console.log('Image URL:', data.url);
}, {
fullPage: false, // true = entire page, false = viewport
mergeTags: {},
});
Server-side via Cloud API (get API key from Dashboard > Project > Settings > API Keys):
const response = await fetch('https://api.unlayer.com/v2/export/image', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Basic ' + Buffer.from('YOUR_API_KEY:').toString('base64'),
},
body: JSON.stringify({
displayMode: 'email',
design: designJSON, // The saved design JSON object
mergeTags: {},
}),
});
const data = await response.json();
// data.url — image URL
Export PDF / ZIP (Paid — Cloud API)
// PDF
unlayer.exportPdf((data) => {
// data.url — PDF URL
// data.design — Design JSON
}, { mergeTags: {} });
// ZIP
unlayer.exportZip((data) => {
// data.url — ZIP URL
// data.design — Design JSON
}, { mergeTags: {} });
Auto-Save Pattern
Design + HTML (recommended):
let saveTimeout;
unlayer.addEventListener('design:updated', () => {
clearTimeout(saveTimeout);
saveTimeout = setTimeout(() => {
unlayer.exportHtml(async (data) => {
await fetch('/api/templates/123', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ design: data.design, html: data.html }),
});
});
}, 1000);
});
Design + HTML + Thumbnail (full):
let saveTimeout;
unlayer.addEventListener('design:updated', () => {
clearTimeout(saveTimeout);
saveTimeout = setTimeout(() => {
// Save design + HTML immediately
unlayer.exportHtml(async (data) => {
await fetch('/api/templates/123', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ design: data.design, html: data.html }),
});
});
// Generate thumbnail (slower, paid — debounce longer or do on manual save)
unlayer.exportImage(async (data) => {
if (!data.url) return;
await fetch('/api/templates/123/thumbnail', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ thumbnailUrl: data.url }),
});
}, { fullPage: false });
}, 3000); // Longer debounce for image generation
});
Design JSON Quick Reference
The design JSON has this structure (see references/design-json.md for full TypeScript types):
JSONTemplate
├── counters — Internal counters
├── schemaVersion — Schema version number
└── body
├── rows[] — Each row contains columns
│ ├── cells[] — Column ratios: [1,1] = 50/50
│ └── columns[]
│ └── contents[] — Content items (text, image, button...)
├── headers[] — Same as rows (with headersAndFooters feature)
├── footers[] — Same as rows
└── values — Body-level styles (backgroundColor, contentWidth, fontFamily)
Content types: text, heading, button, image, divider, social, html, video, menu, timer.
Common Mistakes
| Mistake | Fix |
|---|---|
| Only saving HTML, not design JSON | Always save both — all export methods return data.design |
Calling export before editor:ready | Wait for the event first |
| Not configuring File Storage for image/PDF export | Image and PDF uploads go to your connected File Storage |
| Not debouncing auto-save | design:updated fires on every keystroke — debounce 1-3 seconds |
Ignoring chunks in exportHtml | Use chunks.body when you need just content without <!DOCTYPE> wrapper |
| Missing API key for image/PDF/ZIP | Cloud API key required — get from Dashboard > Project > Settings > API Keys |
Troubleshooting
| Problem | Fix |
|---|---|
exportImage returns error | Check API key, check Cloud API plan, verify design isn't empty |
| Exported HTML looks different from editor | Use cleanup: true (default), check custom CSS |
design:updated fires too often | Always debounce — it fires on every property change |
| Loaded design shows blank | Check schemaVersion compatibility, validate JSON structure |
Resources
Source
git clone https://github.com/unlayer/unlayer-skills/blob/main/unlayer-export/SKILL.mdView on GitHub Overview
Unlayer-export handles HTML, plain text, images, PDFs, and ZIP exports from the Unlayer editor. Some formats run in the browser for free; others use the Cloud API and require a paid plan. Always save the design JSON alongside any export to preserve the ability to edit later.
How This Skill Works
Use dedicated export methods (exportHtml, exportPlainText, exportImage, exportPdf, exportZip) to generate the desired output. Each method returns an object containing the design JSON (data.design) and a format-specific payload (e.g., html, url, or body). For paid formats, server-side Cloud API calls require an API key; client-side exports provide free options.
When to Use It
- You need HTML + design for email sending or web publishing (exportHtml).
- You need a plain text version for SMS or accessibility fallbacks (exportPlainText).
- You need a PNG thumbnail or social preview (exportImage).
- You need a print-ready document (exportPdf).
- You need an offline package of assets (exportZip).
Quick Start
- Step 1: Choose an export method (e.g., exportHtml) based on your goal.
- Step 2: Call the API and save both data.html (or data.url) and data.design to your backend.
- Step 3: When reloading, ensure the editor is ready and call unlayer.loadDesign(saved.design) to restore your work.
Best Practices
- Always save the design JSON alongside any export; all export methods return data.design.
- Choose the export type that matches your use case: HTML for emails/web, Plain Text for SMS, Image for previews, PDF for print, ZIP for offline bundles.
- When exporting HTML, tune output with cleanup, minify, and inlineStyles as needed for your recipient.
- Use data.chunks when you need just the body content or extracted CSS/fonts for embedding.
- Secure CLOUD API keys and monitor quotas for paid export methods.
Example Use Cases
- Newsletter email: exportHtml and persist both generated HTML and the design JSON to your templates service.
- SMS fallback: exportPlainText to send plain text alongside your design.
- Social preview: exportImage to generate a PNG URL for thumbnails and sharing.
- Print-ready doc: exportPdf to obtain a PDF URL plus the design JSON for edits.
- Offline bundle: exportZip to deliver a package of assets and the design JSON.