Maizzle: Craft beautiful HTML emails with Tailwind CSS

Send beautiful HTML emails via NestJS crafted with Maizzle and Tailwind CSS

Authors
Marc Stammerjohann 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 &rarr;</span>
    <!-- ... -->
  </a>
</div>
<!-- ... -->

You'll notice that {{name}} is not displayed at all, instead you see undefined 😤.

Handlebars expressions are missing in email template

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 &rarr;</span>
    <!-- ... -->
  </a>
</div>
<!-- ... -->

Handlebars expressions are ignored from compilation

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.

Table of Contents

Top of Page Comments Related Articles

Related Posts

Find more posts like this one.

Authors
Marc Stammerjohann
October 17, 2022

Codegen REST API types and requests for Angular

Automatic code generation from OpenAPI 3 for Angular
Angular NestJS Read More
Authors
Marc Stammerjohann
July 27, 2022

Downloading files with NestJS

Setup type-safe endpoints for downloading files in your NestJS application.
NestJS Read More
Authors
Marc Stammerjohann
August 26, 2021

NestJS: Type-safe File Uploads

Learn how to apply Swagger decorators for type-safe file upload endpoints.
NestJS Read More
Authors
Marc Stammerjohann
July 27, 2022

OpenAPI for your REST APIs in NestJS

Setup Swagger to generate an OpenAPI documentation for your REST endpoints.
NestJS Read More
Authors
Marc Stammerjohann
July 08, 2022

Send Emails with NestJS

Create Email Templates and send them with nodemailer from your Nest application
NestJS Read More
Authors
Marc Stammerjohann
December 15, 2020

Tailwind CSS Purge: Optimize Angular for Production

Remove unused Tailwind CSS utilities from your Angular production build for best performance
Tailwind CSS Angular Scully Read More
Authors
Marc Stammerjohann
October 29, 2020

Jamstack: Angular + Scully + Tailwind CSS

Use Angular's static site generator Scully and style it with Tailwind CSS
Scully Angular Tailwind CSS Read More
Authors
Marc Stammerjohann
September 12, 2022

Introducing NestJS Prisma Library and Schematics

Library and schematics to add Prisma integration to a NestJS application
NestJS Prisma Read More

Sign up for our newsletter

Sign up for our newsletter to stay up to date. Sent every other week.

We care about the protection of your data. Read our Privacy Policy.