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:
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 -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
| Component | Description |
|---|---|
<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:
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:
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.
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 lineTips 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>