A modern MJML boilerplate for building responsive HTML email templates using Bun, TypeScript, and Biome. Includes built-in support for:
- β MJML compilation
- π§Ό Minification via
html-minifier
- π§© Mustache-style templating (e.g.
{{ colorBgDefault }}
) - π₯οΈ Local preview with variable interpolation
- β‘ Superfast builds with Bun
Example of a compiled MJML email using this boilerplate.
git clone https://github.com/mikezotovdev/mjml-boilerplate.git
cd mjml-boilerplate
bun install
bun run build
β οΈ Note: Thebuild
command does not interpolate variables such as{{ colorBgDefault }}
or{{ firstName }}
. These placeholders are preserved in the final output (inpublic/
) because the email content is expected to be injected server-side at runtime.
bun run preview
This command performs a local build with variables fully interpolated for visual testing and previewing emails.
.
βββ .githooks/ # Git hooks (e.g. pre-push)
βββ biome.json # Biome formatter and linter config
βββ build.const.ts # Build-time constants
βββ build.helpers.ts # Build utilities
βββ build.preview.ts # Preview build (with interpolated variables)
βββ build.templates.ts # MJML -> HTML build logic
βββ postbuild.cleanup.ts # Post-build cleanup script
βββ postbuild.minify.ts # Minify HTML output
βββ dist/ # Preview output (interpolated HTML)
βββ public/ # Final output (minified, placeholders retained)
βββ src/
β βββ assets/
β β βββ images/
β β βββ common/ # Shared image assets
β β βββ subject/ # Subject-specific icons
β βββ common/
β β βββ footer/ # Reusable footer components
β β βββ head/ # MJML attributes, global attributes, banners, etc.
β β βββ header/ # Reusable header component
β βββ templates/ # Your email templates go here (use folders). A few examples are already included.
β βββ EmailConfirmation/
β βββ PasswordChanged/
β βββ PasswordReset/
β βββ SignUpWelcome/
Use Mustache-style syntax in your MJML:
<mj-text>Hello, {{ firstName }}!</mj-text>
Variable | Meaning | Example |
---|---|---|
appLink |
Link to the app | https://app.example.com |
supportTelegram |
Support telegram nickname (not a link, no "@" prefix) | support |
supportEmail |
Support email | [email protected] |
firstName |
First name of the user | Billy |
userEmail |
User email address | [email protected] |
companyName |
Company name | Gachi LTD |
link |
Target action link (e.g. confirmation/reset link) | https://app.example.com/api/verify/email?code=69 |
Variable | Meaning | Example |
---|---|---|
colorTextDefault |
Default text color | #1A1A1A |
colorTextDescription |
Description text color | #6B7780 |
colorTextBrand |
Brand text color | #219b61 |
colorTextBrandDark |
Brand dark text color (used for titles) | #176B44 |
colorTextLink |
Link text color | #1B7F4C |
colorTextButton |
Button text color | #FFFFFF |
colorBgDefault |
Default background color | #FFFFFF |
colorBgBrand |
Brand background color | #219b61 |
colorBgButtonBrand |
Brand button background color | #219b61 |
colorBorderDefault |
Default border color | #D0E4DA |
Icons are stored in @2x resolution to ensure crisp display on retina screens.
Variable | Icon size (width x height) | Example |
---|---|---|
logoUrl |
142x48 | https://...image.png |
emailIconUrl |
48x48 | https://...image.png |
telegramLogoUrl |
48x48 | https://...image.png |
subjectPasswordIconUrl |
80x80 | https://...image.png |
subjectSecurityIconUrl |
80x80 | https://...image.png |
subjectEmailIconUrl |
80x80 | https://...image.png |
subjectWelcomeIconUrl |
80x80 | https://...image.png |
Follow these best practices to ensure your email templates render correctly and build reliably:
β οΈ Keep consistent file depth for all*.mjml
files. MJML cannot reliably resolve includes from files at different nesting levels.- π« Never edit files manually in
public/
β always build from source templates instead. - π§© Use
{{ variableName }}
syntax for all dynamic content. - β»οΈ Put shared MJML blocks in
src/common/
to avoid duplication. - π‘ Wrap conditional logic in HTML comments to prevent MJML from stripping unknown tags:
<!-- {{#if userName}} --> <mj-text>Hello, {{ userName }}!</mj-text> <!-- {{/if}} -->
Script | Description |
---|---|
bun run build |
Clean, compile, and minify templates (no variables interpolated) |
bun run preview |
Full build with live preview of injected variables |
bun run fix |
Format and lint using Biome |
bun run clean-deps |
Remove all dependencies and lockfile |
bun run setup-hooks |
Git pre-push hook setup |
- Bun β ultra-fast JS runtime
- MJML β email markup language
- TypeScript β type-safe scripting
- Biome β code formatter + linter
- html-minifier β for lightweight, production-ready output
Created by Mike Zotov
If you find this project helpful, feel free to support it:
- ERC-20/BNB/Base:
0x117D11A2AD3D084b1aBC2a2F8be9531884936054
- Solana:
7bYonr2pYa3Fs5vh21ngcAVHBUufGnUFp5mBposFMrxz
- Bitcoin:
bc1qm2dydvexnrh7caqc4wz4lasaqwfz8zaez298kd
MIT Β© Mike Zotov