ADR-001: Custom Email Renderer Over MJML

Why Owlat built a custom table-based HTML email renderer instead of using MJML, gaining full control over VML, dark mode, and per-client rendering.

  • Status: Accepted
  • Date: 2024-06-15

Context

Owlat needs to convert a JSON block structure into responsive HTML emails that render correctly across all major email clients (Gmail, Outlook, Apple Mail, Yahoo). MJML is the industry standard for this, providing a markup language that compiles to email-safe HTML.

However, MJML has limitations for our use case:

  • No fine-grained control — MJML abstracts away the underlying table-based HTML, making it difficult to implement advanced features like VML backgrounds, conditional content, or per-client rendering.
  • Bundle size — MJML is a large dependency (~2MB) that would bloat the serverless backend.
  • Block model mismatch — Our editor uses a JSON block model (similar to Notion/Editor.js). Converting blocks to MJML markup only to have MJML convert it to HTML adds an unnecessary translation layer.
  • Extensibility — Adding custom block types in MJML requires writing MJML components that conform to its internal API, which is poorly documented and tightly coupled to its rendering pipeline.

Decision

Build a custom table-based HTML email renderer (@owlat/email-renderer) that converts our JSON block format directly to email-safe HTML.

Consequences

Enables:

  • Direct control over every HTML attribute, enabling VML support for Outlook (bulletproof buttons, background images, gradients)
  • Per-client render simulation (targetClient option) for accurate previews
  • Custom block registry for third-party block types
  • CSS inlining with selective annotations (@inline / @head-only)
  • Dark mode support with per-block overrides
  • AMP email output as a first-class format
  • Email size analysis and compatibility scoring
  • Smaller bundle — the renderer is ~50KB vs MJML's ~2MB

Trade-offs:

  • We own the full complexity of email client quirks (Outlook conditional comments, Gmail CSS stripping, Yahoo spacing bugs)
  • New team members can't rely on MJML documentation — they need to learn our renderer's API
  • We must maintain our own compatibility data instead of relying on MJML's tested output