Maizzle: Craft beautiful HTML emails with Tailwind CSS
Send beautiful HTML emails via NestJS crafted with Maizzle and Tailwind CSS
- Authors
- Marc Stammerjohann
- Published at
Building beautiful HTML emails with CSS can be quite cumbersome 🙁, right?!
Get ready to build beautiful email templates 🖼️ crafted with Maizzle using Tailwind CSS and send them with your NestJS application.
Start with the previous post on how to create email templates and sending them from NestJS with Handlebars.
Maizzle project
Add a new Maizzle project to your NestJS repository.
@maizzle/framework v4.x requires Node v14 or higher
npx degit maizzle/maizzle project-name
# for example
npx degit maizzle/maizzle mail-templates
Open the new project in your terminal and install the dependencies:
cd mail-templates
npm install
Start the development server with npm run dev
and go to localhost:3000 in your browser to see your email templates. This is useful for local development.
When your templates are ready you want to build your email templates for production:
npm run build
You'll get to this in a bit. Next you'll customize the email confirmation template (src/templates/transactional.html
) to include Handlebar expressions.
Open the transaction template in your browser and watch for the changes you'll make.
Handlebar expressions
Ideally, when sending an email you want to customize the email content based on the requesting user. This includes greeting the user 👋 (with name or email), links for interactions (confirming email, requesting new password) or personal links for managing mail preferences.
Open src/templates/transactional.html
and add custom content through Handlebars expressions for name
and url
to the template.
<!-- ... -->
<p>Hello {{name}},</p>
<p class="m-0 ... sm:leading-8">
Is it you we're looking for?
</p>
<p class="m-0 mb-6">
Please confirm your email address by clicking the button below:
</p>
<div class="leading-full">
<a
href="{{url}}"
class="inline-block ... hover:bg-indigo-500"
>
<!-- ... -->
<span class="mso-text-raise-4">Confirm email address →</span>
<!-- ... -->
</a>
</div>
<!-- ... -->
You'll notice that {{name}}
is not displayed at all, instead you see undefined
😤.
Thats because Maizzle itself relies on curly brace expressions to access variables or template front matter.
Luckily, Maizzle provides two options to prevent expression compilation 😎. Use @{{ }}
for single expression and <raw> ... </raw>
tag for blocks with multiple expressions.
Add the @
prefix to the name
and url
expression and you should see Hello {{name}},
in your compiled template.
<!-- ... -->
<p>Hello @{{name}},</p>
<p class="m-0 ... sm:leading-8">
Is it you we're looking for?
</p>
<p class="m-0 mb-6">
Please confirm your email address by clicking the button below:
</p>
<div class="leading-full">
<a
href="@{{url}}"
class="inline-block ... hover:bg-indigo-500"
>
<!-- ... -->
<span class="mso-text-raise-4">Confirm email address →</span>
<!-- ... -->
</a>
</div>
<!-- ... -->
Your email template is looking great. Let's build the template for production and for NestJS to be used for sending emails.
Build production email
Maizzle provides a build command for production to inline CSS and many more optimizations.
npm run build
This compiles your emails by default to mail-templates/build_production
. Let's change the destination path to be located inside your NestJS - src/mail/templates
.
config.production.js
contains settings used for the production build. Open it and change the destination path and change the extension to hbs
.
module.exports = {
build: {
templates: {
destination: {
path: '../src/mail/templates',
extension: 'hbs'
},
},
},
inlineCSS: true,
removeUnusedCSS: true,
}
Now when you run npm run build
your templates will be compiled to src/mail/templates/*.hbs
.
Cool 💪 your templates are ready and compiled for production. Use them in your NestJS application with @nestjs-modules/mailer
.
Sending mail template
In the previous post you created the MailService
.
import { MailerService } from '@nestjs-modules/mailer';
import { Injectable } from '@nestjs/common';
import { User } from './../user/user.entity';
@Injectable()
export class MailService {
constructor(private mailerService: MailerService) {}
async sendUserConfirmation(user: User, token: string) {
const url = `example.com/auth/confirm?token=${token}`;
await this.mailerService.sendMail({
to: user.email,
subject: 'Welcome to Nice App! Confirm your Email',
template: './confirmation', // 👈 either change to ./transactional or rename transactional.html to confirmation.html
context: { // ✏️ filling curly brackets with content
name: user.name,
url,
},
});
}
}
Here you used confirmation
as the template name. Either rename transactional.html
to confirmation.html
in mail-templates/src/templates
, don't forget to compile again, or use transactional
as the template name when using this.mailerService.sendMail({...});
.
Awesome 🤩 you are all set to build your own beautiful email templates. Comment below which templates you most often use in your applications. Checkout these transactional templates build with Maizzle (v3.7.2) for some inspiration.
Do want to dig through the source code? Checkout the repository for this post.
Sponsor us
Did you find this post useful? We at notiz.dev write about our experiences developing Apps, Websites and APIs and develop Open Source tools. Your support would mean a lot to us 🙏. Receive a reward by sponsoring us on Patreon or start with a one-time donation on GitHub Sponsors.