Kraiter
Guides

Templates

Build responsive emails with MJML and Liquid variables for dynamic content.

Templates define the content and layout of your emails. Kraiter uses MJML for responsive email markup and Liquid for dynamic variable interpolation. Every email sent through Kraiter — whether transactional or part of a sequence — uses a template.

Creating a template

Create a template with a name, subject line, and MJML body:

SDK
const template = await kraiter.templates.create({
  name: 'welcome-email',
  subject: 'Welcome to {{ contact.properties.company }}, {{ contact.properties.name }}!',
  body: `
    <mjml>
      <mj-body>
        <mj-section>
          <mj-column>
            <mj-text>
              Hello {{ contact.properties.name }},
            </mj-text>
            <mj-text>
              Thanks for joining us. Your account is ready to go.
            </mj-text>
            <mj-button href="https://app.example.com/getting-started">
              Get Started
            </mj-button>
          </mj-column>
        </mj-section>
      </mj-body>
    </mjml>
  `,
});
cURL
curl -X POST https://api.kraiter.com/templates \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "welcome-email",
    "subject": "Welcome, {{ contact.properties.name }}!",
    "body": "<mjml><mj-body><mj-section><mj-column><mj-text>Hello {{ contact.properties.name }}</mj-text></mj-column></mj-section></mj-body></mjml>"
  }'

MJML basics

MJML is a markup language that compiles to responsive HTML email. It handles the complex table-based layouts that email clients require, so you can focus on content.

Document structure

Every MJML template follows this structure:

<mjml>
  <mj-head>
    <!-- Styles, attributes, fonts -->
  </mj-head>
  <mj-body>
    <mj-section>
      <mj-column>
        <!-- Content components -->
      </mj-column>
    </mj-section>
  </mj-body>
</mjml>

Common components

ComponentDescription
<mj-section>A horizontal row container
<mj-column>A column within a section (max 4 per section)
<mj-text>Text content block
<mj-button>A call-to-action button
<mj-image>An image element
<mj-divider>A horizontal divider line
<mj-spacer>Vertical spacing
<mj-table>A data table

Styling

Use <mj-attributes> in the head to define default styles:

<mj-head>
  <mj-attributes>
    <mj-all font-family="Helvetica, Arial, sans-serif" />
    <mj-text font-size="16px" color="#333333" line-height="1.5" />
    <mj-button background-color="#4F46E5" color="#ffffff" font-size="16px" />
  </mj-attributes>
</mj-head>

Multi-column layouts

Create side-by-side content with multiple columns:

<mj-section>
  <mj-column>
    <mj-image src="https://example.com/product.png" />
  </mj-column>
  <mj-column>
    <mj-text>Product description goes here.</mj-text>
    <mj-button href="https://example.com/buy">Buy Now</mj-button>
  </mj-column>
</mj-section>

Liquid variables

Template subjects and bodies support Liquid syntax for dynamic content. Variables are enclosed in double curly braces: {{ variable }}.

Available variables

Contact properties are available under the contact namespace:

{{ contact.email }}
{{ contact.properties.name }}
{{ contact.properties.plan }}
{{ contact.properties.signupDate }}

Custom variables can be passed at send time (for transactional emails):

{{ variables.resetLink }}
{{ variables.orderTotal }}
{{ variables.invoiceNumber }}

Conditional content

Use Liquid tags to show or hide content based on properties:

{% if contact.properties.plan == 'pro' %}
  <mj-text>Thanks for being a Pro member!</mj-text>
{% else %}
  <mj-text>Upgrade to Pro for advanced features.</mj-text>
{% endif %}

Loops

Iterate over arrays passed as custom variables:

{% for item in variables.items %}
  <mj-text>{{ item.name }} - {{ item.price }}</mj-text>
{% endfor %}

Filters

Liquid filters transform values:

{{ contact.properties.name | upcase }}
{{ contact.properties.signupDate | date: "%d %B %Y" }}
{{ variables.amount | divided_by: 100.0 }}

Updating a template

Update an existing template by its name:

SDK
await kraiter.templates.update('welcome-email', {
  subject: 'Welcome aboard, {{ contact.properties.name }}!',
  body: '<mjml>...</mjml>',
});

When you update a template, the changes take effect immediately for all future sends. Emails already sent or queued are not affected.

Template versioning

Kraiter maintains a version history for each template. Every update creates a new version. When an email is sent, it records which template version was used, so you can always see exactly what was sent.

List template versions:

SDK
const versions = await kraiter.templates.listVersions('welcome-email');

Previewing a template

Use the preview endpoint to render a template with sample data without sending an email. This is useful for testing your Liquid variables and MJML layout.

SDK
const preview = await kraiter.templates.preview('welcome-email', {
  contact: {
    email: 'test@example.com',
    properties: {
      name: 'Test User',
      plan: 'pro',
    },
  },
  variables: {
    resetLink: 'https://example.com/reset/abc123',
  },
});

console.log(preview.html);    // Rendered HTML
console.log(preview.subject); // Rendered subject line

Tips for responsive emails

  • Keep it simple. Single-column layouts work best across all email clients. Use multi-column only when necessary.
  • Use web-safe fonts. Not all email clients support custom fonts. Always include fallback fonts.
  • Optimise images. Large images slow down loading. Compress images and set explicit width/height attributes.
  • Test across clients. Email rendering varies significantly between Gmail, Outlook, Apple Mail, and others. Use the preview endpoint to check your output.
  • Avoid complex CSS. Email clients strip most CSS. MJML handles the hard parts, but avoid relying on advanced CSS features.
  • Set a preheader. Use <mj-preview> in the head to set preview text that appears next to the subject line in inbox views.
<mj-head>
  <mj-preview>Your weekly summary is ready to view.</mj-preview>
</mj-head>