{"id":15353621,"url":"https://github.com/mifi/mjml-dynamic","last_synced_at":"2025-04-15T00:25:49.945Z","repository":{"id":65263929,"uuid":"589131320","full_name":"mifi/mjml-dynamic","owner":"mifi","description":"Dynamic JSON content for MJML templates","archived":false,"fork":false,"pushed_at":"2025-01-18T10:17:05.000Z","size":1176,"stargazers_count":23,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-28T12:21:11.990Z","etag":null,"topics":["email","email-template","mjml","mjml-api","mjml-framework","mjml-template","mjml-to-html"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mifi.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":"2023-01-15T06:43:38.000Z","updated_at":"2025-02-14T11:58:59.000Z","dependencies_parsed_at":"2023-11-30T08:24:33.394Z","dependency_job_id":"b33c4858-3eac-409b-9148-58ffca0172d7","html_url":"https://github.com/mifi/mjml-dynamic","commit_stats":{"total_commits":28,"total_committers":1,"mean_commits":28.0,"dds":0.0,"last_synced_commit":"4d5896872b7c20a216a4bf497870e44f0d823592"},"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mifi%2Fmjml-dynamic","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mifi%2Fmjml-dynamic/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mifi%2Fmjml-dynamic/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mifi%2Fmjml-dynamic/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mifi","download_url":"https://codeload.github.com/mifi/mjml-dynamic/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248982675,"owners_count":21193439,"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":["email","email-template","mjml","mjml-api","mjml-framework","mjml-template","mjml-to-html"],"created_at":"2024-10-01T12:14:44.710Z","updated_at":"2025-04-15T00:25:49.914Z","avatar_url":"https://github.com/mifi.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Dynamic JSON content for MJML templates\n\n![mjml](https://user-images.githubusercontent.com/402547/212595358-e7d16377-381e-442a-8b94-ed4d9e135df7.png)\n\n[MJML](https://github.com/mjmlio/mjml) is a great open source tool for building emails, however as developers we often want to include dynamic content in our emails. The problem is that MJML **does not have any templating support** (and they have no intention to.) This can be worked around by using placeholders in your MJML and then compiling the markup first using a templating engine like [Handlebars](https://github.com/handlebars-lang/handlebars.js), [Pug](https://github.com/pugjs/pug) etc, however what if we could generate the content dynamically using JavaScript! It turns out that MJML already supports rendering [JSON markup](https://documentation.mjml.io/#using-mjml-in-json) instead of MJML. The problem is that there is no way to **include partial JSON content, but `mjml-dynamic` solves this problem.**\n\n## Benefits:\n\n- Easily inject text, tags, lists and more into your MJML programmatically.\n- Replace parts of your MJML document with something dynamically generated.\n- Rewrite `mj-` attributes, text content or child nodes on a per-node basis.\n- No need for [conditionals, loops](https://handlebarsjs.com/guide/builtin-helpers.html) etc. Just use plain JavaScript!\n- No need to escaping special characters or sanitize utrusted data.\n- Don't worry about injection attacks or special characters breaking the layout.\n- You can still design and preview your overall layout using existing MJML tooling.\n- Use in conjuction with [`mjml-react`](https://github.com/Faire/mjml-react) to replace parts of your document with React code.\n\n## How does it work?\n\n### New MJML attribute: `mj-replace-id`\n\nYou include a special tag `mj-replace-id=\"myId\"` into any of your `mj-` tags in your MJML document. This will allow you to process these tags programmatically or inject your own JSON MJML content into them, using a new `replacers` option for the `mjml2html` function.\n\n```xml\n\u003cmjml\u003e\n  \u003cmj-body\u003e\n    \u003cmj-section\u003e\n      \u003cmj-column\u003e\n\n        \u003cmj-button mj-replace-id=\"myId\"\u003e\n          Some text\n        \u003c/mj-button\u003e\n\n      \u003c/mj-column\u003e\n    \u003c/mj-section\u003e\n  \u003c/mj-body\u003e\n\u003c/mjml\u003e\n```\n\n### New `mjml2html` option: `replacers`\n\nThe `mjml2html()` API of `mjml-dynamic` is exactly the same as the upstream `mjml` API, but there is an added option called `replacers` which is an object where each key corresponds to a `mj-replace-id` and the value is either `null` or an object with one or more of the following properties:\n\n| Property | Type | Description |\n| --- | --- | --- |\n| `content` | `string` | Change the text or HTML content of the element. |\n| `attributes` | `object\u003cstring,string\u003e` | Change the HTML attributes of the element. |\n| `children` | `array\u003cobject\u003e` | Change the element's MJML children elements (for example with `mjml-react`.) |\n| `tagName` | `string` | Change the tag to a completely different tag. |\n\nAny of the above `replacers` properties are optional. You may alternatively supply a **function** that receives the existing value as its only argument and returns the new value. This can be used to modify the existing values. **`content` and `attributes` replacements are automatically escaped, however if you specify the functional `content` replacer, the response is not escaped.**\n\nIf you specify `null` as the replacer's value instead of the above object, the matching node *will be removed*.\n\n## Examples\n\n```\nyarn add -D mjml mjml-dynamic\n```\n\n```js\nimport mjml2html from 'mjml-dynamic';\n\nconst replacers = {\n  myId: {\n    tagName: 'mj-button',\n    // these values are all automatically escaped:\n    content: 'new text content', \n    attributes: { color: 'red', href: '\u0026' },\n  },\n  removedId: null, // use null to completey remove the node\n  // ... more `mj-replace-id`s\n};\n\nconst { html } = mjml2html(mjml, { replacers });\n```\n\nThis will output the equivalent of the following MJML document:\n\n```xml\n\u003cmjml\u003e\n  \u003cmj-body\u003e\n    \u003cmj-section\u003e\n      \u003cmj-column\u003e\n\n        \u003cmj-button color=\"red\" href=\"\u0026amp;\"\u003e\n          new text content\n        \u003c/mj-button\u003e\n\n      \u003c/mj-column\u003e\n    \u003c/mj-section\u003e\n  \u003c/mj-body\u003e\n\u003c/mjml\u003e\n```\n\n### Example: functional `replacers`\n\n```js\nconst replacers = {\n  myId: {\n    // Add an additional color attribute, while preserving the existing attributes:\n    attributes: (attributes) =\u003e ({ ...attributes, color: 'red' },\n\n    // Rewrite the HTML content:\n    content: (content) =\u003e content.replace('{{something}}', 'something else'),\n    // NOTE! mjml-dynamic does not automatically escape the replacement HTML here, so you need to do it yourself.\n    // (You may use a template engine like handlebars for more sophisticated replacements)\n  },\n};\n```\n\n### Example: list / loop (replacing `children`):\n\nYou can use this to include any kind of dynamic MJML as a node's `children`, and even loops to generate lists.\n\n```xml\n\u003cmjml\u003e\n  \u003cmj-body mj-replace-id=\"peopleList\" /\u003e\n\u003c/mjml\u003e\n```\n\n```js\nimport mjml2html from 'mjml-dynamic';\n\nconst people = [\n  { name 'John Doe', age: 23 },\n  { name 'Jane Doe', age: 34 },\n  { name 'Satoshi Nakamoto', age: 99 },\n]\n\nconst replacers = {\n  peopleList: {\n    children: [{\n      tagName: 'mj-section',\n      children: [{\n        tagName: 'mj-column',\n        children: people.map(({ name, age }) =\u003e ({\n          tagName: 'mj-text',\n          content: `${name} - ${age} yr`,\n        })),\n      }],\n    }],\n  },\n};\n\nconst { html } = mjml2html(xml, { replacers });\n```\n\nThis will output the equivalent of the following MJML document:\n```xml\n\u003cmjml\u003e\n  \u003cmj-body\u003e\n    \u003cmj-section\u003e\n      \u003cmj-column\u003e\n        \u003cmj-text\u003e\n          John Doe - 23 yr\n        \u003c/mj-text\u003e\n      \u003c/mj-column\u003e\n    \u003c/mj-section\u003e\n    \u003cmj-section\u003e\n      \u003cmj-column\u003e\n        \u003cmj-text\u003e\n          Jane Doe - 34 yr\n        \u003c/mj-text\u003e\n      \u003c/mj-column\u003e\n    \u003c/mj-section\u003e\n    \u003cmj-section\u003e\n      \u003cmj-column\u003e\n        \u003cmj-text\u003e\n          Satoshi Nakamoto - 99 yr\n        \u003c/mj-text\u003e\n      \u003c/mj-column\u003e\n    \u003c/mj-section\u003e\n  \u003c/mj-body\u003e\n\u003c/mjml\u003e\n```\n\n### Practical example: usage with [`mjml-react`](https://github.com/Faire/mjml-react)\n\nExample to replace parts of your `.mjml` document with React code:\n\nAssuming you have a `template.mjml` with an overall email layout that you can preview using official MJML tooling:\n\n```xml\n\u003cmjml\u003e\n  \u003cmj-body\u003e\n    \u003cmj-include path=\"header.mjml\" /\u003e\n\n    \u003cmj-section\u003e\n      \u003cmj-column\u003e\n        \u003cmj-text\u003eHere is a list of people\u003c/mj-text\u003e\n      \u003c/mj-column\u003e\n    \u003c/mj-section\u003e\n\n    \u003cmj-section\u003e\n      \u003cmj-column mj-replace-id=\"peopleList\" /\u003e\n    \u003c/mj-section\u003e\n\n    \u003cmj-include path=\"footer.mjml\" /\u003e\n  \u003c/mj-body\u003e\n\u003c/mjml\u003e\n```\n\nThen you can render the contents of the element `\u003cmj-column mj-replace-id=\"peopleList\"\u003e` using `mjml-react`:\n\n```jsx\nimport readFile from 'fs/promises';\nimport { parseXml } from 'mjml-dynamic';\n\nconst tableData = [\n  { id: 1, name: 'Jane Doe' },\n  { id: 2, name: 'John Doe' },\n];\n\nconst mjml = renderToStaticMarkup((\n  \u003cMjmlTable\u003e\n    {tableData.map(({ id, name }) =\u003e (\n      \u003ctr key={id}\u003e\n        \u003ctd style={{ padding: '0 10px' }}\u003e\n          {name}\n        \u003c/td\u003e\n      \u003c/tr\u003e\n    )}\n  \u003c/MjmlTable\u003e\n));\n\nconst { json: reactJson } = parseXml(mjml);\n\nconst xml = await readFile('template.mjml', 'utf-8');\n\nconst replacers = {\n  peopleList: {\n    children: [reactJson],\n  },\n};\n\nconst { html } = mjml2html(xml, { replacers });\n```\n\n### [More examples](./index.test.mjs)\n\n## Other options\n\n### `validateReplacers` (default `true`)\n\nWhether to check if all `mj-replace-id` in the MJML tree are specified in options, and check if all `replacers` exist in the MJML tree. This is useful in order to reduce the chance of coding mistakes.\n\n## TODO\n- Support running in the browser\n\n## References\n\nSee initial discussion here: https://github.com/mjmlio/mjml/discussions/2619\n\n\n## Other cool MJML packages\n\n- [`mjml-react`](https://github.com/Faire/mjml-react)\n- [`mjml-json`](https://github.com/lenfestlab/mjml-json)\n- [`react-mailkit`](https://github.com/pavkout/react-mailkit)\n- [`easy-email`](https://github.com/zalify/easy-email)\n\n## Release\n\n```\nnp\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmifi%2Fmjml-dynamic","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmifi%2Fmjml-dynamic","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmifi%2Fmjml-dynamic/lists"}