Jump to content



Recommended Posts


Github: https://github.com/eprcstudio/PageMjmlToHtml
Modules directory: https://processwire.com/modules/page-mjml-to-html/

A module allowing you to write your Processwire template using MJML and get a converted HTML output using MJML API.

This is considered to be in alpha and as such needs some testing before being used in production!

Capture d’écran 2021-12-02 à 19 05 54


Created by Mailjet, MJML is a markup language making it a breeze to create newsletters displayed consistently across all email clients.

Write your template using MJML combined with Processwire’s API and this module will automatically convert your code into a working newsletter thanks to their free-to-use Rest API.


For this module to work you will need to get an API key and paste it in the module’s configuration.


Once your credentials are validated, select the template(s) in which you’re using the MJML syntax, save and go visualize your page(s) to see if everything’s good. You will either get error/warning messages or your email properly formatted and ready-to-go.

From there you can copy/paste the raw generated code in an external mailing service or distribute your newsletter using ProMailer.


  • The MJML output is cached to avoid repetitive API calls
    • Not cached if there are errors/warnings
    • Cleared if the page is saved
    • Cleared if the template file has been modified
  • A simple (dumb?) code viewer highlights lines with errors/warnings
  • A button is added to quickly copy the raw code of the generated newsletter
    • Not added if the page is rendered outside of a PageView
    • Only visible to users with the page’s edit permission
  • A shortcut is also added under “View” in the edit page to open the raw code in a new tab
  • Multi-languages support

Capture d’écran 2021-12-02 à 21 21 45


The code viewer is only shown to superusers. If there’s an error the page will display:

  • Only its title for guests
  • Its title and a message inviting to contact the administrator for editors

If you are using the markup regions output strategy, it might be best to not append files to preserve your MJML markup before calling the MJML API. This option is available in the module’s settings.

  • Like 12
  • Thanks 4
Link to comment
Share on other sites

As this module was just recommend to me I had to give it a try.

Got the necessary API keys and grabbed the latest copy from Github. The result so far:


Double checked the entered credentials, picked some templates and ...



I'm running the latest PW 3.0.189 here and have to remove the module to bring the site back to live.

  • Like 1
Link to comment
Share on other sites

The first part with the errors on initial save has been solved but I still need to find an alternative for the second part. In more recent dev versions Ryan removed a function I was relying on in my code (namely ProcessPageView::getPage) and I need to look for an alternative.

I'll publish an update once it's all good.

  • Like 3
Link to comment
Share on other sites

That looks way better and works almost as expected... at least in the frontend.


Trying the EDIT page option results in an error.


Full error:

DEBUG MODE BACKTRACE ($config->debug == true):
#0 /home/aca/www/pw.test/wire/core/Page.php(1808): ProcessWire\Wire->___callUnknown()
#1 /home/aca/www/pw.test/wire/core/Wire.php(420): ProcessWire\Page->___callUnknown()
#2 /home/aca/www/pw.test/wire/core/WireHooks.php(951): ProcessWire\Wire->_callMethod()
#3 /home/aca/www/pw.test/wire/core/Wire.php(485): ProcessWire\WireHooks->runHooks()
#4 /home/aca/www/pw.test/wire/core/Wire.php(488): ProcessWire\Wire->__call()
#5 /home/aca/www/pw.test/site/modules/PageMjmlToHtml-main/PageMjmlToHtml.module(444): ProcessWire\Wire->__call()
#6 /home/aca/www/pw.test/wire/core/Wire.php(417): ProcessWire\PageMjmlToHtml->addViewRaw()
#7 /home/aca/www/pw.test/wire/core/WireHooks.php(1062): ProcessWire\Wire->_callMethod()
#8 /home/aca/www/pw.test/wire/core/Wire.php(485): ProcessWire\WireHooks->runHooks()
#9 /home/aca/www/pw.test/wire/modules/Process/ProcessPageEdit/ProcessPageEdit.module(631): ProcessWire\Wire->__call()
#10 /home/aca/www/pw.test/wire/modules/Process/ProcessPageEdit/ProcessPageEdit.module(573): ProcessWire\ProcessPageEdit->renderEdit()
#11 /home/aca/www/pw.test/wire/core/Wire.php(414): ProcessWire\ProcessPageEdit->___execute()
#12 /home/aca/www/pw.test/wire/core/WireHooks.php(951): ProcessWire\Wire->_callMethod()
#13 /home/aca/www/pw.test/wire/core/Wire.php(485): ProcessWire\WireHooks->runHooks()
#14 /home/aca/www/pw.test/wire/core/ProcessController.php(337): ProcessWire\Wire->__call()
#15 /home/aca/www/pw.test/wire/core/Wire.php(414): ProcessWire\ProcessController->___execute()
#16 /home/aca/www/pw.test/wire/core/WireHooks.php(951): ProcessWire\Wire->_callMethod()
#17 /home/aca/www/pw.test/wire/core/Wire.php(485): ProcessWire\WireHooks->runHooks()
#18 /home/aca/www/pw.test/wire/core/admin.php(160): ProcessWire\Wire->__call()
#19 /home/aca/www/pw.test/site/templates/admin.php(16): require('/home/aca/www/p...')
#20 /home/aca/www/pw.test/wire/core/TemplateFile.php(327): require('/home/aca/www/p...')
#21 /home/aca/www/pw.test/wire/core/Wire.php(414): ProcessWire\TemplateFile->___render()
#22 /home/aca/www/pw.test/wire/core/WireHooks.php(951): ProcessWire\Wire->_callMethod()
#23 /home/aca/www/pw.test/wire/core/Wire.php(485): ProcessWire\WireHooks->runHooks()
#24 /home/aca/www/pw.test/wire/modules/PageRender.module(571): ProcessWire\Wire->__call()
#25 /home/aca/www/pw.test/wire/core/Wire.php(417): ProcessWire\PageRender->___renderPage()
#26 /home/aca/www/pw.test/wire/core/WireHooks.php(951): ProcessWire\Wire->_callMethod()
#27 /home/aca/www/pw.test/wire/core/Wire.php(485): ProcessWire\WireHooks->runHooks()
#28 /home/aca/www/pw.test/wire/core/WireHooks.php(1059): ProcessWire\Wire->__call()
#29 /home/aca/www/pw.test/wire/core/Wire.php(485): ProcessWire\WireHooks->runHooks()
#30 /home/aca/www/pw.test/wire/modules/Process/ProcessPageView.module(172): ProcessWire\Wire->__call()
#31 /home/aca/www/pw.test/wire/modules/Process/ProcessPageView.module(114): ProcessWire\ProcessPageView->renderPage()
#32 /home/aca/www/pw.test/wire/core/Wire.php(417): ProcessWire\ProcessPageView->___execute()
#33 /home/aca/www/pw.test/wire/core/WireHooks.php(951): ProcessWire\Wire->_callMethod()
#34 /home/aca/www/pw.test/wire/core/Wire.php(485): ProcessWire\WireHooks->runHooks()
#35 /home/aca/www/pw.test/index.php(55): ProcessWire\Wire->__call()
#36 {main}


  • Like 1
  • Sad 1
Link to comment
Share on other sites

I published a new update with the following changes:

  • changed filename of css/js files to PageMjmlToHtml (instead of debug)
  • changed module configuration value name templates to allowedTemplates to avoid confusion
  • modified the js so the copy to clipboard code works on all browser
  • modified getCacheName method so it also checks for LanguageSupport and LanguageSupportPageNames

Regarding point 2: please double-check the "Templates to convert (...)" field in the module settings properly catched up your previous value

Thanks again @wbmnfktr for his thorough tests!

  • Thanks 1
Link to comment
Share on other sites

I pushed a new release fixing an issue from the previous update: I forgot to update the name of the inputfield as well when switching from templates to allowedTemplates, resulting in an issue when trying to add/remove templates.

You might need to uninstall / reinstall the module, though it shouldn't be an issue to do so.

  • Like 3
Link to comment
Share on other sites

I pushed a new version with two additions:

  • the first one adds the ability to select specific roles to bypass the cache and thus convert the MJML code on each page render. I don’t think it will be used much but it’s there in case the cache invalidation when saving the page or updating the template’s file is not enough
  • the second one is more interesting as it adds the ability to generate a unique output per GET variable. Previously a GET variable would just output and save to the page’s cache but now you can specify GET variables (or the wildcard "*") to have a cached version per variable. Please note though that, when rendering, if there are several GET variables only the first one is used. `raw` is ignored as it’s used by the module. My current use-case for this is having a "browser" version (with ?browser) where I can display a newsletter with the right webfont.

I also added a small note in the settings regarding TracyDebugger to invite module users to disable the panel bar for the MJML templates. TracyDebugger hooks to `render` as well and thus some of its markup might end up in the converted code when showing the `raw` version.

I think by now this can be considered "beta", though please test on your local environment first and let me know if you notice anything.

  • Like 2
Link to comment
Share on other sites

  • 1 month later...

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Create New...