DEV Community

Cover image for Send Emails in node js using nodemailer, grandjs, and JSX components
tareksalem
tareksalem

Posted on • Updated on

Send Emails in node js using nodemailer, grandjs, and JSX components

Building server-side applications became easier after the revolution of single-page apps which clear the hassle from your backend code and your backend is no longer render views, and the UI is being rendered from the frontend, which gives your backend the freedom from using template engine and let your backend to focus on your REST APIs only.

This sounds good for most cases but about if you have a blog or eCommerce website and want to send emails to your users contains the latest news, products or even send to them a reminder for something, here the need to a template engine which you need to integrate it with nodemailer to render data inside it and then send this template to your users.
In this case, the first thing you will think about which template engine you can you use to accomplish this task without being a pain in rendering the data inside your template and do all of this frontend stuff, however you may be in the right way but you should be noted that the age of template engines in the backend has gone, and now is the age of JSX!

What is JSX

Simply JSX is a syntax that enables you to write HTML elements inside the javascript file without any need to use normal static HTML files or even use a template engine to render dynamic data inside HTML elements. It was introduced for the first time by reactjs to render HTML elements from javascript for your frontend application to build a single-page app.

Now JSX is not only used in frontend development but it also can be used inside your backend application. One of these frameworks that use JSX inside the backend is Grandjs which is a full backend framework that enables you to write JSX components to do server-side rendering or to use these components to render as mail templates!

About Grandjs

Grandjs is a full backend framework that enables you to build solid server-side applications based on nodejs, js, and typescript, and one of these powerful features is using JSX components to render HTML elements either for SSR or for sending email templates to your clients!
You can find the full documentation from here

What we will do?

In this article, I will just show you how to render an email template using nodemailer, JSX, and grandjs.

Note
You can use grandjs with any other server framework you use if you want to use it just for sending email templates

Install Dependencies

Just run the following command to install the following dependencies:

  • nodemailer
  • grandjs
npm i --save nodemailer grandjs dotenv
Enter fullscreen mode Exit fullscreen mode

Then your project structure can be as the following

  • index.js
  • views
  • Mailer.js
  • data.json
  • .env

In index.js you just need to import View class from grandjs to set its configurations as the following:

const {View} = require("grandjs")

// set configuration for views

View.settings.set("views", "./views")
Enter fullscreen mode Exit fullscreen mode

Then create a new component in views directory, this component will be called Newsletter.jsx

Note
the name of the component should be with .jsx extension
In each component you define in a file you should import View class as the following:

const {View} = require("grandjs");
Enter fullscreen mode Exit fullscreen mode

After that we will define our styles as js object as we do in reactjs

const Styles = {
    body: {
        backgroundColor:"#ccc",
        margin:"0px",
        padding:"0px"
    },
    section_one: {
        padding:"30px",
        margin:0
    },
    container: {
        maxWidth: "600px",
        margin:"0 auto",
        backgroundColor:"white",
        fontSize:"0px",
        padding:"0px",
        fontFamily:"'Roboto',sans-serif",

    },
    header: {
        textAlign:"center",
        height:"50px",
        padding:"0px",
        margin:"0px",

    },
    headerline: {
        backgroundColor:"#E6FFF7",
        textAlign:"center",
        fontSize:"20px",
        color: "#333",
        lineHeight: "40px",
        fontWeight:"400px",
        margin:"0px",
    },
    img: {
        display:"inline",
        width:"25%",
        verticalAlign:"middle",
    },
    paragraph: {
        display:"inline-block",
        fontSize:"14px",
        fontWeight:"300",
        color:"#666",
        width:"calc(75% - 40px)",
        padding:"0 20px",
        margin:"0",
        lineHeight:1.4,
        verticalAlign:"middle",

    },
    btn: {
        display:"block",
        backgroundColor:"#29F0C2",
        fontSize:"18px",
        color:"white",
        padding:0,
        margin:"30px auto 0",
        border:0,
        borderRadius:"5px",
        lineHeight:"40px",
        height:"40px",
        width: "200px",
        textAlign:"center"
    }

}
Enter fullscreen mode Exit fullscreen mode

Then you can define your jsx component as a functional component as the following:

const {View} = require("grandjs");

const NewsLetter = () => {
     <div>
      <body style={Styles.body}>
        <div
        style={Styles.container}
        >
          <div
          style={Styles.header}
          >
            <h1>Your daily News is here!</h1>
          </div>
          {data.news.map((item) => {
              return (
                <div>
                <h2
                style={Styles.headerline}
                >
                  {item.title}
                </h2>
                <div class="section_one" style={Styles.section_one}>
                  <img
                    src={item.img}
                    style={Styles.img}
                  />
                  <div
                    class="paragraph"
                    style={Styles.paragraph}
                  >
                    {item.description}
                  </div>
                  <a
                    href={item.link}
                    class="btn"
                    style={Styles.btn}
                  >
                    Read Article
                  </a>
                </div>
              </div>

              )
          })}          
          </div>
      </body>
    </div>

}
Enter fullscreen mode Exit fullscreen mode

So the final file would be as the following:

const { View } = require("grandjs");


const Styles = {
    body: {
        backgroundColor:"#ccc",
        margin:"0px",
        padding:"0px"
    },
    section_one: {
        padding:"30px",
        margin:0
    },
    container: {
        maxWidth: "600px",
        margin:"0 auto",
        backgroundColor:"white",
        fontSize:"0px",
        padding:"0px",
        fontFamily:"'Roboto',sans-serif",

    },
    header: {
        textAlign:"center",
        height:"50px",
        padding:"0px",
        margin:"0px",

    },
    headerline: {
        backgroundColor:"#E6FFF7",
        textAlign:"center",
        fontSize:"20px",
        color: "#333",
        lineHeight: "40px",
        fontWeight:"400px",
        margin:"0px",
    },
    img: {
        display:"inline",
        width:"25%",
        verticalAlign:"middle",
    },
    paragraph: {
        display:"inline-block",
        fontSize:"14px",
        fontWeight:"300",
        color:"#666",
        width:"calc(75% - 40px)",
        padding:"0 20px",
        margin:"0",
        lineHeight:1.4,
        verticalAlign:"middle",

    },
    btn: {
        display:"block",
        backgroundColor:"#29F0C2",
        fontSize:"18px",
        color:"white",
        padding:0,
        margin:"30px auto 0",
        border:0,
        borderRadius:"5px",
        lineHeight:"40px",
        height:"40px",
        width: "200px",
        textAlign:"center"
    }

}
const Newsletter = ({data}) => {
  return (
    <div>
      <body style={Styles.body}>
        <div
        style={Styles.container}
        >
          <div
          style={Styles.header}
          >
            <h1>Your daily News is here!</h1>
          </div>
          {data.news.map((item) => {
              return (
                <div>
                <h2
                style={Styles.headerline}
                >
                  {item.title}
                </h2>
                <div class="section_one" style={Styles.section_one}>
                  <img
                    src={item.img}
                    style={Styles.img}
                  />
                  <div
                    class="paragraph"
                    style={Styles.paragraph}
                  >
                    {item.description}
                  </div>
                  <a
                    href={item.link}
                    class="btn"
                    style={Styles.btn}
                  >
                    Read Article
                  </a>
                </div>
              </div>

              )
          })}          
          </div>
      </body>
    </div>

    );
};

module.exports = Newsletter;
Enter fullscreen mode Exit fullscreen mode

After that, I will create a file called data.json, this file will includes the mails that we want to send to and the news that we want to send as the following:

{
    "users": ["user@email.com"],
    "news": [
        {
            "title": "React Infinite Scroll Tutorial: With and Without a Library",
            "img": "https://res.cloudinary.com/practicaldev/image/fetch/s--a8DByl-W--/c_imagga_scale,f_auto,fl_progressive,h_420,q_auto,w_1000/https://res.cloudinary.com/practicaldev/image/fetch/s--1QH-jSvc--/c_imagga_scale%2Cf_auto%2Cfl_progressive%2Ch_420%2Cq_auto%2Cw_1000/https://dev-to-uploads.s3.amazonaws.com/i/ttz2sso79x6cl7chdjml.jpg",
            "description": "Infinite scroll is a modern web & application design concept that loads content continuously as the user scrolling down the page. It changes the function of pagination.",
            "link": "https://dev.to/syakirurahman/react-infinite-scroll-tutorial-with-and-without-a-library-1abg"
        },
        {
            "title": "React App with Tailwind CSS / Emotion / Twin Macro",
            "img": "https://res.cloudinary.com/practicaldev/image/fetch/s--lmHWjUIc--/c_imagga_scale,f_auto,fl_progressive,h_420,q_auto,w_1000/https://res.cloudinary.com/practicaldev/image/fetch/s--atZRMw7r--/c_imagga_scale%2Cf_auto%2Cfl_progressive%2Ch_420%2Cq_auto%2Cw_1000/https://dev-to-uploads.s3.amazonaws.com/i/zbydh2m62o81na2n5fq8.png",
            "description": "I'll explain how install and configure Tailwind CSS / Emotion and twin.macro in a Single Project of React to improve the way we use styles in our projects.",
            "link": "https://dev.to/angelcodes/react-app-with-tailwind-css-emotion-twin-macro-3dpe"
        },
        {
            "title": "Currying in JavaScript",
            "img": "https://res.cloudinary.com/practicaldev/image/fetch/s--UvT9Kb3S--/c_imagga_scale,f_auto,fl_progressive,h_420,q_auto,w_1000/https://res.cloudinary.com/practicaldev/image/fetch/s--vw8755uu--/c_imagga_scale%2Cf_auto%2Cfl_progressive%2Ch_420%2Cq_auto%2Cw_1000/https://dev-to-uploads.s3.amazonaws.com/i/viplwlivvz3xxahdycac.png",
            "description": "Currying is a process in functional programming in which we can transform a function with multiple arguments into a sequence of nesting functions. It returns a new function that expects the next argument inline.",
            "link": "https://dev.to/suprabhasupi/currying-in-javascript-1k3l"
        }
    ]
}
Enter fullscreen mode Exit fullscreen mode

Then we will create Mailer.js which will be a class responsible for sending emails
Firstly we will import View class from grandjs, View has a built-in method called importJsx that enables you to import JSX component into javascript file as the following:

const { View } = require("grandjs");
const NewsLetter = View.importJsx("./views/Newsletter.jsx");
Enter fullscreen mode Exit fullscreen mode

Then we will require data.json file that contains our data:

const data = require("./data.json");

Enter fullscreen mode Exit fullscreen mode

We will use Google account to send the emails and the mailer configuration will be something like the following:

class Mailer{

    constructor() {
        this.config = {
            secure: true,
            service: "Gmail",
            host: "smtp.gmail.com",
            port: 465,
            auth: {
                user: process.env.MailUsername,
                pass: process.env.MailPassword
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

As you can see, we read user and pass from process.env because we will pass these secret data as environment variables in the .env file and load this file using dotenv package

Then we will create a method inside Mailer class, this method will be called sendNewsLetter as the following:

async sendNewsLetter() {
        try {
            console.log(data.users, this.config)
            const transporter = NodeMailer.createTransport(this.config);
            let template = View.renderToHtml(NewsLetter, {data})
            const mailOptions = {
                from: this.config.auth.user,
                to: data.users,
                subject: "Daily News",
                html: template,
            };
            await transporter.sendMail(mailOptions);
            console.log("mail sent successfully");
        } catch(err) {
            console.log(err);
        }
    }
Enter fullscreen mode Exit fullscreen mode

As you can see above we use another function inside View, this function called renderToHtml which enables you to convert the written JSX component into normal HTML elements as a string, then you will be able to send this HTML string as mail template.

Then we specified the mail options such as from which means from which email that this mail is sent, to, which represents the targeted emails and this can be a string or an array, and the subject and finally the HTML template
The final Mailer file can as the following:

const { View } = require("grandjs");
const NodeMailer = require("nodemailer");
const data = require("./data.json");
const NewsLetter = View.importJsx("./views/Newsletter.jsx");

class Mailer{

    constructor() {
        this.config = {
            secure: true,
            service: "Gmail",
            host: "smtp.gmail.com",
            port: 465,
            auth: {
                user: process.env.MailUsername,
                pass: process.env.MailPassword
            }
        }
    }
    async sendNewsLetter() {
        try {
            console.log(data.users, this.config)
            const transporter = NodeMailer.createTransport(this.config);
            let template = View.renderToHtml(NewsLetter, {data})
            const mailOptions = {
                from: this.config.auth.user,
                to: data.users,
                subject: "Daily News",
                html: template,
            };
            await transporter.sendMail(mailOptions);
            console.log("mail sent successfully");
        } catch(err) {
            console.log(err);
        }
    }
}


module.exports = new Mailer();
Enter fullscreen mode Exit fullscreen mode

Then in index.js we will update it to load our .env file as the following:

require("dotenv").config();
const {View} = require("grandjs")

View.settings.set("views", "./views");
Enter fullscreen mode Exit fullscreen mode

Then import the Mailer.js file as the following:

const Mailer = require("./Mailer");
Enter fullscreen mode Exit fullscreen mode

Then just call sendNewsLetter function to send the mail

require("dotenv").config();
const {View} = require("grandjs")

View.settings.set("views", "./views");

const Mailer = require("./Mailer");

Mailer.sendNewsLetter();
Enter fullscreen mode Exit fullscreen mode

Now you just need to create .env file and define the following variables inside it:

MailUsername=your_sender_email
MailPassword=your_sender_email_password

Enter fullscreen mode Exit fullscreen mode

Now you can run your application by giving the following command:

node index
Enter fullscreen mode Exit fullscreen mode

the mail template is sent now!

Mail Temaplate

Top comments (3)

Collapse
 
steve-lebleu profile image
Steve Lebleu

Thanks for the sharing. If you search something very flexible, who's allow you to deal with SMTP and / or web api provider, using templates or just a buffer, take a look to this one: github.com/steve-lebleu/cliam

Collapse
 
salah856 profile image
Salah Elhossiny

Nice

Collapse
 
omariohasan profile image
Omar Hasan

Amazing Article