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
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")
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");
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"
}
}
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>
}
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;
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"
}
]
}
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");
Then we will require data.json
file that contains our data:
const data = require("./data.json");
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
}
}
}
}
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);
}
}
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();
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");
Then import the Mailer.js
file as the following:
const Mailer = require("./Mailer");
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();
Now you just need to create .env
file and define the following variables inside it:
MailUsername=your_sender_email
MailPassword=your_sender_email_password
Now you can run your application by giving the following command:
node index
the mail template is sent now!
Top comments (3)
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
Nice
Amazing Article