{"id":15117208,"url":"https://github.com/Boostport/mjml-go","last_synced_at":"2025-09-27T23:30:49.239Z","repository":{"id":37455029,"uuid":"498715276","full_name":"Boostport/mjml-go","owner":"Boostport","description":"Compile MJML to HTML directly in your Go applications!","archived":false,"fork":false,"pushed_at":"2024-09-18T22:43:07.000Z","size":75392,"stargazers_count":85,"open_issues_count":1,"forks_count":6,"subscribers_count":4,"default_branch":"main","last_synced_at":"2024-11-14T20:54:01.913Z","etag":null,"topics":["go","golang","mjml","mjml-to-html","webassembly"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Boostport.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-06-01T11:51:09.000Z","updated_at":"2024-11-07T10:20:42.000Z","dependencies_parsed_at":"2024-06-07T04:35:30.962Z","dependency_job_id":null,"html_url":"https://github.com/Boostport/mjml-go","commit_stats":null,"previous_names":[],"tags_count":20,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Boostport%2Fmjml-go","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Boostport%2Fmjml-go/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Boostport%2Fmjml-go/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Boostport%2Fmjml-go/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Boostport","download_url":"https://codeload.github.com/Boostport/mjml-go/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":234470271,"owners_count":18838628,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["go","golang","mjml","mjml-to-html","webassembly"],"created_at":"2024-09-26T01:45:51.996Z","updated_at":"2025-09-27T23:30:49.233Z","avatar_url":"https://github.com/Boostport.png","language":"Go","funding_links":[],"categories":["Tools"],"sub_categories":[],"readme":"# mjml-go\n[![Go Reference](https://pkg.go.dev/badge/github.com/Boostport/mjml-go.svg)](https://pkg.go.dev/github.com/Boostport/mjml-go)\n[![Tests Status](https://github.com/Boostport/mjml-go/workflows/Tests/badge.svg)](https://github.com/Boostport/mjml-go)\n[![Code Coverage](https://qlty.sh/gh/Boostport/projects/mjml-go/coverage.svg)](https://qlty.sh/gh/Boostport/projects/mjml-go)\n\nCompile [MJML](https://mjml.io/) into HTML directly in your Go application!\n\n## Why?\n[MJML](https://github.com/mjmlio/mjml) is a JavaScript library. In order to use it with other languages,\nthe usual approach is to wrap the library in a Node.js HTTP server and provide an endpoint through which\napplications not written in JavaScript can make HTTP requests to compile MJML into HTML.\n\nThis approach poses some challenges, for example, if MJML is upgraded to a new major version in\nthe deployed Node.js servers, applications calling these servers will need to be upgraded in a synchronized\nmanner to avoid incompatibilities. In addition, running these extra servers introduces extra moving parts\nand the network into the mix.\n\nThis is why we built `mjml-go` and created an idiomatic Go API to compile MJML into HTML directly in Go applications that\ncan be deployed as a single Go binary.\n\n## How?\nWe wrote a [simple JavaScript wrapper](js/src) that wraps around the MJML library by accepting input and returning output\nusing JSON. This wrapper is then bundled using webpack and compiled into a WebAssembly module using Suborbital's [Javy fork](https://github.com/suborbital/javy),\na Javascript to WebAssembly compiler. The WebAssembly module is then compressed using Brotli to yield a 10x reduction in \nfile size.\n\nDuring runtime, the module is decompressed and loaded into a [Wazero](https://github.com/tetratelabs/wazero) runtime \non application start up to accept input in order to compile MJML into HTML.\n\n### Workers\nAs WebAssembly modules compiled using Javy are not thread-safe and cannot be called concurrently, the library maintains\na pool of 1 to 10 instances to perform compilations. Idle instances are automatically destroyed and will be re-created when\nthey are needed. This means that the library is thread-safe and you can use it concurrently in multiple goroutines.\n\n## Example\n```go\nfunc main() {\n\t\n\tinput := `\u003cmjml\u003e\u003cmj-body\u003e\u003cmj-section\u003e\u003cmj-column\u003e\u003cmj-divider border-color=\"#F45E43\"\u003e\u003c/mj-divider\u003e\u003cmj-text font-size=\"20px\" color=\"#F45E43\" font-family=\"helvetica\"\u003eHello World\u003c/mj-text\u003e\u003c/mj-column\u003e\u003c/mj-section\u003e\u003c/mj-body\u003e\u003c/mjml\u003e`\n\t\n\toutput, err := mjml.ToHTML(context.Background(), input, mjml.WithMinify(true))\n\t\n\tvar mjmlError mjml.Error\n\t\n\tif errors.As(err, \u0026mjmlError){\n\t    fmt.Println(mjmlError.Message)\n\t    fmt.Println(mjmlError.Details)\t\n\t}\n\t\n\tfmt.Println(output)\n}\n```\n\n## Options\nThe library provides a complete list of options to customize the MJML compilation process including options for\n`html-minifier`, `js-beautify` and `juice`.\n\nThese are all exposed via an idiomatic Go API and a complete list can be found in the [Go documentation](https://pkg.go.dev/github.com/Boostport/mjml-go).\n\n### Defaults\nIf beautify and minify are enabled, but no options were passed in, the library defaults to using the same defaults\nas the MJML CLI application:\n\nFor minify:\n\n| option                  | value   |\n|-------------------------|---------|\n| `CaseSensitive`         | `true`  |\n| `CollapseWhitespace`    | `true`  |\n| `MinifyCSS`             | `false` |\n| `RemoveEmptyAttributes` | `true`  |\n\nFor beautify:\n\n| option                     | value   |\n|----------------------------|---------|\n| `EndWithNewline`           | `true`  |\n| `IndentSize`               | `2`     |\n| `PreserveNewlines`         | `false` |\n| `WrapAttributesIndentSize` | `2`     |\n\n## Limitations\nThe WebAssembly module is not able to access the filesystem, so `\u003cmj-include\u003e` tags are ignored. The solution is to\nflatten your templates during development and pass the flattened templates to `mjml.ToHTML()`.\n\nThis [example](https://github.com/mjmlio/mjml/issues/2465#issuecomment-1109515536) provides a good starting point to\ncreate a Node.js script to do this:\n```javascript\nimport mjml2html from 'mjml' // load default component\nimport components from 'mjml-core/lib/components.js'\nimport Parser from 'mjml-parser-xml'\nimport jsonToXML from 'mjml-core/lib/helpers/jsonToXML.js'\n\nconst xml = `\u003cmjml\u003e...\u003c/mjml\u003e`\n\nconst mjml = Parser(xml, {\n      components,\n      filePath: '.',\n      actualPath: '.'\n    })\n\nconsole.log(JSON.stringify(mjml))\nconsole.log(jsonToXML(mjml))\n```\n\n## Differences from the MJML JavaScript library\n- Beautify and minify will be removed from the library in [MJML5](https://github.com/mjmlio/mjml/pull/2204) and will be\nmoved into the MJML CLI. Therefore, to prepare for this move, the [wrapper](js/src) imports `html-minifier`\nand `js-beautify` directly to support minifying and beautifying the output.\n- In the current implementation of mjml, it is not possible to customize the output of `js-beautify`. In this library,\nwe have exposed those options.\n\n## Benchmarks\nWe are benchmarking against a very [minimal Node.js server](js/src/server.js) serving a single API endpoint.\n```\ngoos: linux\ngoarch: amd64\npkg: github.com/Boostport/mjml-go\ncpu: 12th Gen Intel(R) Core(TM) i7-12700F\nBenchmarkNodeJS/black-friday-20                      524           2137932 ns/op\nBenchmarkNodeJS/one-page-20                          246           4574134 ns/op\nBenchmarkNodeJS/reactivation-email-20                180           6386099 ns/op\nBenchmarkNodeJS/real-estate-20                       144           8674627 ns/op\nBenchmarkNodeJS/recast-20                            154           7289670 ns/op\nBenchmarkNodeJS/receipt-email-20                     295           4112735 ns/op\nBenchmarkMJMLGo/black-friday-20                       31          35718282 ns/op\nBenchmarkMJMLGo/one-page-20                           14          84422520 ns/op\nBenchmarkMJMLGo/reactivation-email-20                 15          73484300 ns/op\nBenchmarkMJMLGo/real-estate-20                         7         170549075 ns/op\nBenchmarkMJMLGo/recast-20                              8         139803444 ns/op\nBenchmarkMJMLGo/receipt-email-20                      15          71913776 ns/op\nPASS\nok      github.com/Boostport/mjml-go    27.786s\n```\n\nIn its current state the Node.js implementation is significantly faster than `mjml-go`. However, with improvements to\nWazero (in particular [tetratelabs/wazero#618](https://github.com/tetratelabs/wazero/issues/618) and [tetratelabs/wazero#179](https://github.com/tetratelabs/wazero/issues/179)),\nmodule instantiation times should see great improvement, reducing worker spin-up times and improving the compilation performance.\n\nAlso, we should see improvements from Javy improve these numbers as well.\n\n## Development\n\n### Run tests\nYou can run tests using docker by running `docker compose run test` from the root of the repository.\n\n### Run benchmarks\nFrom the root of the repository, run `go test -bench=. ./...`. Alternatively, you can run them in a docker container:\n`docker compose run benchmark`\n\n### Compile WebAssembly module and build Node.js test server\nRun `docker compose run build-js` from the root of the repository.\n\n## Other languages\nSince the MJML library is compiled into a WebAssembly module, it should be relatively easy to take the compiled module and\ndrop it into languages with WebAssembly environments.\n\nIf you've created a library for another language, please let us know, so that we can add it to this list!","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FBoostport%2Fmjml-go","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FBoostport%2Fmjml-go","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FBoostport%2Fmjml-go/lists"}