Skip to content

Quick Start

1. Install packages

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

2. Mount the editor

html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Templatical Editor</title>
  <style>
    body { margin: 0; }
    #editor { height: calc(100vh - 48px); }
    #toolbar { height: 48px; display: flex; align-items: center; padding: 0 16px; border-bottom: 1px solid #e5e7eb; }
    #toolbar button { padding: 8px 16px; background: #1a73e8; color: #fff; border: none; border-radius: 6px; cursor: pointer; }
  </style>
</head>
<body>
  <div id="toolbar">
    <button onclick="save()">Save Template</button>
  </div>
  <div id="editor"></div>

  <script type="module">
    import { init } from '@templatical/editor';
    import '@templatical/editor/style.css';

    const editor = await init({
      container: '#editor',
    });

    window.save = async function () {
      // The JSON is the source of truth. The MJML is an optional cache —
      // worth keeping only because this example renders in the browser.
      // See "What to store" in How rendering works.
      const content = editor.getContent();
      const mjml = await editor.toMjml();

      await fetch('/api/templates', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ content, mjml }),
      });
    };
  </script>
</body>
</html>

Your backend receives both the JSON (store it to let users edit later) and the MJML. toMjml() does not produce HTML.

3. Compile MJML to HTML

On the server, compile the MJML you just posted with any MJML library — Node, PHP, Python, Ruby, and others. How Rendering Works lists them. The Node package is mjml:

bash
npm install mjml
bash
pnpm add mjml
bash
yarn add mjml
bash
bun add mjml
ts
import mjml2html from "mjml";

const { html } = mjml2html(mjml);
// html is ready to send

From a saved JSON file, without mounting the editor (also needs mjml):

bash
npx -y @templatical/template-tools render template.json --format html -o email.html
bash
pnpm dlx @templatical/template-tools render template.json --format html -o email.html
bash
yarn dlx @templatical/template-tools render template.json --format html -o email.html
bash
bunx @templatical/template-tools render template.json --format html -o email.html

See Template Tools.

Shadow DOM by default

The editor mounts inside a Shadow DOM, so host page CSS cannot cascade into editor elements. Use a <div> — or any shadow-host-eligible element — as the container; elements like <table>, <form>, or <input> cannot host a shadow root.

Pass shadowDom: false to opt out if you need an unusual container, target editor internals from document.querySelector, or support Firefox <101 / Safari <16.4. See the Shadow DOM guide for the full trade-off list and theming via :host.

Next steps