PHPackages                             php-mjml/php-mjml - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Templating &amp; Views](/categories/templating)
4. /
5. php-mjml/php-mjml

ActiveLibrary[Templating &amp; Views](/categories/templating)

php-mjml/php-mjml
=================

PHP port of the MJML email templating library

v0.0.1(2mo ago)1827↓50%MITPHPPHP &gt;=8.2CI passing

Since Jan 29Pushed 2mo agoCompare

[ Source](https://github.com/php-mjml/php-mjml)[ Packagist](https://packagist.org/packages/php-mjml/php-mjml)[ RSS](/packages/php-mjml-php-mjml/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependencies (8)Versions (4)Used By (0)

  [![PHP-MJML](demo/assets/images/php-mjml-logo.png)](demo/assets/images/php-mjml-logo.png) PHP-MJML
========

[](#php-mjml)

 Native PHP port of the [MJML](https://mjml.io) email templating library.
 Convert MJML markup into responsive HTML emails without Node.js.  Warning

**Work in Progress** — This library is currently under active development with [Claude Code](https://claude.ai/code) and is not yet ready for production use. APIs may change without notice. Use with care.

   [ ![PHP-MJML Live Demo](docs/live-demo.png) ](https://php-mjml.on-forge.com)  ### Try it in your browser

[](#try-it-in-your-browser)

Edit MJML markup and see the rendered HTML email output in real time — no installation required.

 [ ![Try it Live](https://camo.githubusercontent.com/c9e26b29bf3c55e39f43e00554dd5bd14496dfcd49ce88a82f131d16fb7d63e2/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f2546302539462539412538305f5472795f69745f4c6976652d44656d6f2d626c75653f7374796c653d666f722d7468652d6261646765266c6162656c436f6c6f723d34613135346226636f6c6f723d303037626666267363616c653d32) ](https://php-mjml.on-forge.com)  **Key Features:**

- Zero JavaScript dependencies — pure PHP implementation
- Parity tested against the official MJML library
- PHP 8.2+ with strict typing (PHPStan level 8)

Installation
------------

[](#installation)

```
composer require php-mjml/php-mjml
```

Usage
-----

[](#usage)

```
use PhpMjml\Renderer\Mjml2Html;
use PhpMjml\Parser\MjmlParser;
use PhpMjml\Component\Registry;
use PhpMjml\Preset\CorePreset;

$registry = new Registry();
$registry->registerMany(CorePreset::getComponents());

$renderer = new Mjml2Html($registry, new MjmlParser());

$mjml =  'https://fonts.googleapis.com/css?family=Open+Sans:300,400,500,700',
        'Custom Font' => 'https://example.com/custom-font.css',
    ],
);

$result = $renderer->render($mjml, $options);
```

Post-Processing (Minify, Beautify)
----------------------------------

[](#post-processing-minify-beautify)

Like the JavaScript MJML library, post-processing (minification, beautification) is not handled by the core library. We recommend using dedicated tools:

### Minification

[](#minification)

```
composer require pfaciana/tiny-html-minifier
```

```
use TinyHtmlMinifier\TinyMinify;

$result = $renderer->render($mjml);
$minified = TinyMinify::html($result->html, ['collapse_whitespace' => true]);
```

### Beautification

[](#beautification)

```
composer require gajus/dindent
```

```
use Gajus\Dindent\Indenter;

$result = $renderer->render($mjml);
$indenter = new Indenter();
$beautified = $indenter->indent($result->html);
```

### Stripping Comments

[](#stripping-comments)

To remove HTML comments while preserving Outlook conditional comments:

```
$html = preg_replace('//s', '', $result->html);
```

Security
--------

[](#security)

When processing untrusted content (user input, external APIs), use the built-in sanitizer:

```
use PhpMjml\Security\EmailContentSanitizer;

$sanitizer = new EmailContentSanitizer();
$safeContent = $sanitizer->sanitize($untrustedHtml);

$mjml = "{$safeContent}";
```

See [docs/SECURITY.md](docs/SECURITY.md) for comprehensive security guidance.

Available Components
--------------------

[](#available-components)

### Body Components

[](#body-components)

ComponentDescription`mj-body`Root container for email content`mj-section`Horizontal section with background support`mj-column`Column within a section (auto-width distribution)`mj-group`Groups columns together for consistent mobile behavior`mj-wrapper`Wraps multiple sections with shared background`mj-text`Text content with full typography control`mj-button`Call-to-action button`mj-image`Responsive image`mj-divider`Horizontal divider line`mj-spacer`Vertical spacing`mj-table`HTML table for tabular data`mj-social`Social media icon links`mj-social-element`Individual social media icon`mj-navbar`Navigation bar`mj-navbar-link`Navigation link`mj-hero`Hero section with background image`mj-carousel`Image carousel/slideshow`mj-carousel-image`Individual carousel image`mj-accordion`Expandable accordion container`mj-accordion-element`Individual accordion item`mj-accordion-title`Accordion item title`mj-accordion-text`Accordion item content`mj-raw`Raw HTML passthrough### Head Components

[](#head-components)

ComponentDescription`mj-head`Container for head elements`mj-title`Email title (shown in browser tab)`mj-preview`Preview text (shown in inbox)`mj-attributes`Default attribute values`mj-breakpoint`Responsive breakpoint configuration`mj-font`Custom web font registration`mj-style`Custom CSS styles`mj-html-attributes`Add attributes to rendered HTML elementsRequirements
------------

[](#requirements)

- PHP 8.2+
- Extensions: `dom`, `libxml`

Development
-----------

[](#development)

```
# Run all checks (style, static analysis, tests)
composer run ca

# Individual commands
composer run test          # All tests
composer run test:unit     # Unit tests
composer run test:parity   # Parity tests (requires npx mjml)
composer run cs:fix        # Fix code style
composer run phpstan       # Static analysis
```

### Parity Testing

[](#parity-testing)

Tests compare PHP output against the official MJML CLI to ensure identical HTML generation:

```
composer run test:parity
```

Requires Node.js with MJML available via `npx mjml`.

Architecture: Why XML Parsing?
------------------------------

[](#architecture-why-xml-parsing)

PHP-MJML parses MJML as **XML** rather than HTML. The main reason: HTML5 parsers do not honor self-closing syntax on custom elements. `` would be parsed as an unclosed `` tag, breaking the tree structure. XML handles this correctly.

The tradeoff is that XML is stricter than HTML — it rejects things like duplicate attributes, bare `&` characters, and HTML-named entities (`&nbsp;`). The parser includes a preprocessing pipeline to bridge these gaps:

1. **Ending-tag extraction** — Content inside tags like `` (which may contain raw HTML) is replaced with safe placeholders before XML parsing, then restored afterward.
2. **Attribute deduplication** — Duplicate attributes (e.g., `font-size` declared twice) are reduced to the first occurrence, matching HTML behavior.
3. **Entity conversion** — HTML entities like `&nbsp;` are converted to XML-compatible numeric equivalents (`&#160;`), and bare `&` characters are escaped.

An alternative would be switching to the HTML5 parser (`Dom\HTMLDocument`, available since PHP 8.4), which handles all of the above natively. However, that would require its own preprocessing step to expand self-closing custom tags into explicit open/close pairs. For now, the XML approach with targeted fixups is the simpler path.

Contributing
------------

[](#contributing)

This library was developed with AI assistance. Contributions are welcome — especially new component implementations!

See `CLAUDE.md` for architecture details and component implementation guidelines.

License
-------

[](#license)

MIT

###  Health Score

40

—

FairBetter than 88% of packages

Maintenance83

Actively maintained with recent releases

Popularity22

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity39

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~18 days

Total

2

Last Release

86d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/6abaff8e3d8d647d1cd8d3479bb3793b16a66ee24877d195e697a646439730ba?d=identicon)[dgorges](/maintainers/dgorges)

---

Top Contributors

[![davidgorges](https://avatars.githubusercontent.com/u/3885619?v=4)](https://github.com/davidgorges "davidgorges (71 commits)")

---

Tags

email-marketingmjmlmjml-to-htmlphp

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/php-mjml-php-mjml/health.svg)

```
[![Health](https://phpackages.com/badges/php-mjml-php-mjml/health.svg)](https://phpackages.com/packages/php-mjml-php-mjml)
```

###  Alternatives

[craftcms/cms

Craft CMS

3.6k3.6M2.6k](/packages/craftcms-cms)[symfony/form

Allows to easily create, process and reuse HTML forms

2.8k152.1M2.8k](/packages/symfony-form)[contao/core-bundle

Contao Open Source CMS

1231.6M2.4k](/packages/contao-core-bundle)[ec-cube/ec-cube

EC-CUBE EC open platform.

78527.0k1](/packages/ec-cube-ec-cube)[sinnbeck/laravel-dom-assertions

106250.5k8](/packages/sinnbeck-laravel-dom-assertions)[mmikkel/retcon

Powerful Twig filters for mutating and querying HTML

79183.1k11](/packages/mmikkel-retcon)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
