Keplars

MJML

Build responsive emails with MJML and Keplars Mail Service

MJML is a markup language designed to simplify responsive email coding. It uses semantic tags that automatically compile to responsive HTML that works across all email clients.

Why MJML?

  • Responsive by Default: Components automatically adapt to mobile and desktop screens
  • Semantic Syntax: Clean, intuitive tags that describe what you're building
  • Email Client Compatibility: Generated HTML works on Gmail, Outlook, Apple Mail, and more
  • Component Library: Pre-built components for common email patterns
  • Simple Learning Curve: If you know HTML, you can learn MJML in minutes

Installation

Install MJML in your Node.js project:

npm install mjml

Basic Structure

Every MJML email follows this structure:

<mjml>
  <mj-head>
    <!-- Metadata and styles -->
  </mj-head>
  <mj-body>
    <!-- Email content -->
  </mj-body>
</mjml>

Core Components

mj-section

Represents a row in your email layout. Sections stack vertically.

<mj-section>
  <!-- Columns go here -->
</mj-section>

mj-column

Creates columns within a section. Columns automatically stack on mobile devices.

<mj-section>
  <mj-column>
    <!-- Content goes here -->
  </mj-column>
  <mj-column>
    <!-- Content goes here -->
  </mj-column>
</mj-section>

mj-text

Displays text content with HTML formatting support.

<mj-text font-size="20px" color="#333">
  Hello World
</mj-text>

mj-button

Creates a clickable call-to-action button.

<mj-button href="https://example.com" background-color="#007bff">
  Get Started
</mj-button>

mj-image

Displays responsive images that scale properly.

<mj-image width="200px" src="https://example.com/logo.png" />

Basic Example

Here's a simple welcome email:

<mjml>
  <mj-body>
    <mj-section background-color="#f0f0f0" padding="20px">
      <mj-column>
        <mj-image width="150px" src="https://example.com/logo.png" />
      </mj-column>
    </mj-section>

    <mj-section background-color="#ffffff" padding="20px">
      <mj-column>
        <mj-text font-size="24px" font-weight="bold" color="#333">
          Welcome to Our Service!
        </mj-text>
        <mj-text font-size="16px" color="#666" line-height="24px">
          Thanks for signing up. We're excited to have you on board.
        </mj-text>
        <mj-button href="https://example.com/get-started" background-color="#007bff">
          Get Started
        </mj-button>
      </mj-column>
    </mj-section>
  </mj-body>
</mjml>

Using with Node.js

Compile MJML to HTML and send with Keplars Mail Service:

import mjml2html from 'mjml';

const mjmlTemplate = `
  <mjml>
    <mj-body>
      <mj-section>
        <mj-column>
          <mj-text font-size="20px">
            Hello {{name}}!
          </mj-text>
          <mj-text>
            Your order #{{orderId}} has been confirmed.
          </mj-text>
          <mj-button href="{{trackingUrl}}">
            Track Your Order
          </mj-button>
        </mj-column>
      </mj-section>
    </mj-body>
  </mjml>
`;

// Replace variables
const personalizedMjml = mjmlTemplate
  .replace('{{name}}', 'John')
  .replace('{{orderId}}', '12345')
  .replace('{{trackingUrl}}', 'https://example.com/track/12345');

// Compile MJML to HTML
const { html } = mjml2html(personalizedMjml);

// Send with Keplars Mail Service
await fetch('https://api.keplars.com/api/v1/send-email/async', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.KEPLERS_API_KEY}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    to: ['[email protected]'],
    subject: 'Order Confirmation',
    body: html
  })
});

Using with Template Files

Store MJML templates as files and load them dynamically:

import { readFile } from 'fs/promises';
import mjml2html from 'mjml';

async function sendOrderConfirmation(orderData: any) {
  // Load MJML template
  const mjmlTemplate = await readFile('./templates/order-confirmation.mjml', 'utf-8');

  // Replace variables
  let personalizedMjml = mjmlTemplate;
  for (const [key, value] of Object.entries(orderData)) {
    personalizedMjml = personalizedMjml.replace(
      new RegExp(`{{${key}}}`, 'g'),
      String(value)
    );
  }

  // Compile to HTML
  const { html } = mjml2html(personalizedMjml);

  // Send email
  const response = await fetch('https://api.keplars.com/api/v1/send-email/async', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.KEPLERS_API_KEY}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      to: [orderData.email],
      subject: `Order #${orderData.orderId} Confirmed`,
      body: html
    })
  });

  return response.json();
}

Express.js Integration

Create an email service with MJML templates:

import express from 'express';
import mjml2html from 'mjml';

const app = express();

app.post('/send-welcome-email', async (req, res) => {
  const { email, name } = req.body;

  const mjmlTemplate = `
    <mjml>
      <mj-body>
        <mj-section>
          <mj-column>
            <mj-text font-size="24px">Welcome ${name}!</mj-text>
            <mj-text>Thanks for joining our community.</mj-text>
            <mj-button href="https://example.com/dashboard">
              Go to Dashboard
            </mj-button>
          </mj-column>
        </mj-section>
      </mj-body>
    </mjml>
  `;

  const { html } = mjml2html(mjmlTemplate);

  const emailResponse = await fetch('https://api.keplars.com/api/v1/send-email/async', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.KEPLERS_API_KEY}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      to: [email],
      subject: 'Welcome!',
      body: html
    })
  });

  const result = await emailResponse.json();
  res.json({ success: true, messageId: result.messageId });
});

Advanced Features

Custom Fonts

Add custom fonts in the mj-head section:

<mjml>
  <mj-head>
    <mj-font name="Roboto" href="https://fonts.googleapis.com/css?family=Roboto" />
  </mj-head>
  <mj-body>
    <mj-section>
      <mj-column>
        <mj-text font-family="Roboto">
          Text with custom font
        </mj-text>
      </mj-column>
    </mj-section>
  </mj-body>
</mjml>

Styling

Use mj-attributes for global styles:

<mjml>
  <mj-head>
    <mj-attributes>
      <mj-text font-size="14px" color="#333" />
      <mj-button background-color="#007bff" color="#fff" />
    </mj-attributes>
  </mj-head>
  <mj-body>
    <!-- Your content inherits these styles -->
  </mj-body>
</mjml>

Conditional Content

Use mj-include for reusable components:

<!-- header.mjml -->
<mj-section>
  <mj-column>
    <mj-image src="logo.png" />
  </mj-column>
</mj-section>

<!-- main email -->
<mjml>
  <mj-body>
    <mj-include path="./header.mjml" />
    <!-- Rest of email -->
  </mj-body>
</mjml>

Development Workflow

CLI Preview

Preview MJML files in the browser:

mjml watch template.mjml --output output.html

VS Code Extension

Install the MJML extension for syntax highlighting and live preview in VS Code.

Common Email Patterns

Two-Column Layout

<mj-section>
  <mj-column width="50%">
    <mj-image src="product1.jpg" />
    <mj-text>Product 1</mj-text>
  </mj-column>
  <mj-column width="50%">
    <mj-image src="product2.jpg" />
    <mj-text>Product 2</mj-text>
  </mj-column>
</mj-section>

Hero Section

<mj-section background-url="hero.jpg" background-size="cover" padding="100px 0">
  <mj-column>
    <mj-text font-size="40px" color="#fff" align="center">
      Big Announcement
    </mj-text>
    <mj-button background-color="#fff" color="#000">
      Learn More
    </mj-button>
  </mj-column>
</mj-section>
<mj-section background-color="#f0f0f0" padding="20px">
  <mj-column>
    <mj-text font-size="12px" color="#666" align="center">
      © 2025 Your Company. All rights reserved.
    </mj-text>
    <mj-text font-size="12px" align="center">
      <a href="{{unsubscribeUrl}}">Unsubscribe</a>
    </mj-text>
  </mj-column>
</mj-section>

MJML automatically handles responsive design. Two-column layouts stack vertically on mobile devices without any extra code.

Best Practices

Use Semantic Components: Use mj-button instead of styled mj-text links for better email client compatibility.

Mobile-First Thinking: Test how your layout stacks on mobile. MJML handles responsiveness automatically, but preview your emails.

Keep It Simple: Email clients have limited CSS support. MJML abstracts this away, but avoid overly complex layouts.

Test Across Clients: Use services like Litmus or Email on Acid to test your emails across different email clients.

Resources

Official Documentation: https://documentation.mjml.io

Component Reference: https://documentation.mjml.io/#components

MJML Playground: https://mjml.io/try-it-live

VS Code Extension: Search "MJML" in VS Code Extensions

Next Steps

  • React Email - Component-based email templates with React
  • Handlebars - Simple dynamic email templates
  • Send Emails - Learn more about sending emails with Keplars

On this page