Transactional Emails are Automated With Reactjs

Transactional Emails are Automated  With Reactjs

Transactional emails are automated, realtime messages that are sent to users through email after a specific action has been performed on a website or application.

Transactional emails are automated, realtime messages that are sent to users through email after a specific action has been performed on a website or application. These emails are often used for a wide range of alerts such as account creation, password resets, order confirmations, invoices, shipping notifications and more.

It is necessary for every business to have some sort of monitoring service for transactional emails so that you can easily detect when there’s a problem, such as when your emails keep ending up in the spam folder or when critical emails are not being delivered as expected.

In this tutorial, I’ll show you how to set up realtime monitoring of transactional emails with Pusher Channels and SendGrid. We’ll be using the scenario of a password reset email in this example, but you can easily extend it to fit other use cases.


Previous experience with React and Node is required to be able to follow through with this tutorial. You also need to have Node (v8 or later) and npm installed on your computer. You can view the instructions for how to install or upgrade your Node install here.

Sign up for Channels

Before we write any code, let’s sign up for Pusher Channels and grab the necessary credentials. Once you have signed up, select Create Channels app on the sidebar to create a new app. Give your app a name and hit Create my app. Once your app is created, navigate to the API Keys tab and take note of the credentials. We’ll make use of it soon.

Sign up for SendGrid

Create a free account at Once you're in, select Settings > API Keys on the sidebar and then create a new API key. Give your API key a name, and select Full Access under API Key Permissions. Once your key is created, keep it in view until after we’ve added it to an .env file in the next section.

Getting started

Create a new directory for this project and cd into it. Next, run npm init -y to initialize the project with a package.json file. Following that, run the command below to install the dependencies which we’ll be needing to build the server part of the application.

    npm install express cors pusher @sendgrid/mail dotenv --save

Once the dependencies have been installed, create a new variables.env file and populate it with the following contents:

    // variables.env
    PUSHER_APP_ID=<your app id>
    PUSHER_APP_KEY=<your app key>
    PUSHER_APP_SECRET=<your app secret>
    PUSHER_APP_CLUSTER=<your app cluster>
    SENDGRID_API_KEY=<your sendgrid api key>

Next, create a new server.js file and populate it with the following contents:

    // server.js

    require('dotenv').config({ path: 'variables.env' });

    const express = require('express');
    const bodyParser = require('body-parser');
    const cors = require('cors');
    const Pusher = require('pusher');
    const sgMail = require('@sendgrid/mail')


    const app = express();

    app.use(bodyParser.urlencoded({ extended: true }));

    const pusher = new Pusher({
      appId: process.env.PUSHER_APP_ID,
      key: process.env.PUSHER_APP_KEY,
      secret: process.env.PUSHER_APP_SECRET,
      cluster: process.env.PUSHER_APP_CLUSTER,
      useTLS: true,
    });"/reset-password", (req, res) => {
      const { email } = req.body;
      const msg = {
        to: email,
        from: '[email protected]',
        subject: 'Reset your password',
        html: `
          <p>We recently received a request to reset the password for your account.
    Simply click the button below to reset your password.</p>
          <button>Reset Password</button>

        .then(() => {
        .catch((err) => {
          res.status(500).send("An error occured");

    app.set('port', process.env.PORT || 5000);
    const server = app.listen(app.get('port'), () => {
      console.log(`Express running → PORT ${server.address().port}`);

Our server currently has a single route which expects an email address and sends a password reset email message to it via SendGrid’s API.

You can then start the server by running node server.js in the terminal. It should become available on port 5000.

Set up the application frontend

We’ll be making use of the create-react-app tool to bootstrap our React app, so make sure you have it installed on your machine. If not, you can install it globally using npm install -g create-react-app.

By now, the create-react-app command should be available on your machine. Run the following command in the terminal to create a new React app.

    create-react-app client

As soon has the command has finished running, cd into the client directory and run the command below to install the following additional dependencies which we’ll be using to build our application frontend.

    npm install react-router-dom react-toastify axios pusher-js react-spinkit skeleton-css --save

Once all the dependencies have been installed, run npm start to start the development server. The application should be viewable at http://localhost:3000.

Create the password reset form

Open up client/src/index.js in your editor and change it to look like this:

    // client/src/index.js

    import React from 'react';
    import ReactDOM from 'react-dom';
    import { HashRouter, Route } from 'react-router-dom';
    import './index.css';
    import App from './App';
    import * as serviceWorker from './serviceWorker';

        <Route exact path="/" component={App} />
    , document.getElementById('root'));


Next, modify the contents of client/src/App.js as follows:

    // client/src/App.js

    import React from 'react';
    import axios from 'axios';
    import logo from './logo.svg';
    import Spinner from 'react-spinkit';
    import { ToastContainer, toast } from 'react-toastify';

    import 'react-toastify/dist/ReactToastify.min.css';
    import 'skeleton-css/css/normalize.css';
    import 'skeleton-css/css/skeleton.css';
    import './App.css';

    class App extends React.Component {
      state = {
        emailAddress: '',
        loading: false,

      handleChange = (event) => {
        const { value } =;
          emailAddress: value,

      handleSubmit = (event) => {

        const { emailAddress } = this.state;

          loading: true,
        });"http://localhost:5000/reset-password", {
          email: emailAddress,
        }).then(() => {
          toast.success("Check your email address for the recovery link")
          .catch(() => {
            toast.error("An problem occured, please try again later")
          .finally(() => {
              loading: false,

      render() {
        const { emailAddress, loading } = this.state;

        return (
          <div className="App">
            <header className="App-header">
              <img src={logo} className="App-logo" alt="logo" />
              <form onSubmit={this.handleSubmit} className="password-reset" action="">
                <h5>Forgot your password? Reset it below</h5>
                <label htmlFor="email-input">Email address:</label>
                <input type="email" onChange={this.handleChange} value={emailAddress} className="email-input" placeholder="[email protected]" id="email-input" name="email" />
                <button type="submit">Email me a recovery link</button>
                { loading ? <Spinner name='line-scale-pulse-out' /> : null }
              <ToastContainer />


    export default App;

Finally, update client/src/App.css as shown below:

    // client/src/App.css

    .App {
      text-align: center;

    .App-logo {
      animation: App-logo-spin infinite 20s linear;
      height: 40vmin;
      pointer-events: none;

    .App-header {
      background-color: #282c34;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      font-size: calc(10px + 2vmin);
      color: white;
      margin-bottom: 20px;

    .App-link {
      color: #61dafb;

    @keyframes App-logo-spin {
      from {
        transform: rotate(0deg);
      to {
        transform: rotate(360deg);

    form {
      width: 100%;
      max-width: 400px;
      margin: 0 auto;
      text-align: left;

    input, label {
      display: block;
      margin-bottom: 10px;

    .email-input {
      margin-bottom: 20px;

This part of the application is pretty simple. We have a single input where the user will enter their email address. Once the submit button is clicked, a POST request is made to the /reset-password endpoint which we set up earlier on the server.

Now, if we get a successful response (meaning an email was sent successfully), the app displays a toast notification informing the user to go check their email. Otherwise, an error message is displayed on the page.

Set up the monitoring dashboard

The next step is to set up a realtime monitoring dashboard that enables you to have a constant supervision on your transactional feeds.

Create a new Dashboard.js file in client/src and paste the following code into it:

    // client/src/Dashboard.js

    import React, { Component } from 'react';
    import Pusher from 'pusher-js';

    import './Dashboard.css';

    class Dashboard extends Component {
      state = {
        processed: 0,
        dropped: 0,
        delivered: 0,
        deferred: 0,
        bounce: 0,

      componentDidMount() {
        const pusher = new Pusher('<your app key>', {
          cluster: '<your app cluster>',
          forceTLS: true

        var channel = pusher.subscribe('email-events');
        channel.bind('new-event', data => {
          const { event } = data;
          this.setState(prevState => {
            return {
              [event]: prevState[event] + 1

      render() {
        const { processed, dropped, delivered, deferred, bounce } = this.state;
        return (
          <div className="Dashboard">
            <h1>Realtime Transactional Email Monitoring</h1>
            <div className="metrics">

            <div className="metric">
              <span className="metric-name">Emails Processed</span>
              <span className="metric-value">{processed}</span>
          <div className="metric">
          <span className="metric-name">Emails Delivered</span>
          <span className="metric-value">{delivered}</span>
          <div className="metric">
              <span className="metric-name">Emails Dropped</span>
              <span className="metric-value">{dropped}</span>
          <div className="metric">
              <span className="metric-name">Emails Deferred</span>
              <span className="metric-value">{deferred}</span>
          <div className="metric">
              <span className="metric-name">Bounced Emails</span>
              <span className="metric-value">{bounce}</span>

    export default Dashboard;

If you look at the componentDidMount() hook, you can see that we’re making use of the subscribe() method from Pusher to subscribe to a new channel called email-events. Finally, we’re listening for the new-event event on the email-events channel using the bind method and update the application state once we receive a new event on the channel.

Don’t forget to replace <your app id> and <your app cluster> with the appropriate values from your Channels dashboard.

Next, create the CSS file for the Dashboard component as shown below:

    // client/src/Dashboard.css

    .Dashboard {
      padding-top: 30px;
      text-align: center;

    h1 {
      margin-bottom: 50px;

    .metrics {
      width: 1000px;
      margin: 0 auto;
      display: flex;
      justify-content: space-between;
      flex-wrap: wrap;

    .metric {
      width: 130px;
      border-radius: 4px;
      border: 1px solid #ddd;
      display: flex;
      flex-direction: column;

    .metric span {
      display: block;

    .metric-value {
      font-size: 50px;

Finally, update your client/src/index.js file as shown below:

    // client/src/index.js

    import React from 'react';
    import ReactDOM from 'react-dom';
    import { HashRouter, Route } from 'react-router-dom';
    import './index.css';
    import App from './App';
    import Dashboard from './Dashboard';
    import * as serviceWorker from './serviceWorker';

        <Route exact path="/" component={App} />
        <Route exact path="/dash" component={Dashboard} />
      </HashRouter>// If you want your app to work offline and load faster, you can change
    // unregister() to register() below. Note this comes with some pitfalls.
    // Learn more about service workers:
    , document.getElementById('root'));


Trigger updates from the server

The SendGrid Event Webhook provides realtime feedback on every email you send across nine event types. Once an email is sent, SendGrid triggers a bunch of events on a provided HTTP POST URL. We can then use the information contained in the events to update our monitoring dashboard with Channels.

First let’s set up the route on the server. Add this below the /reset-password route:

    // server.js"/events", (req, res) => {
      const events = req.body;
      events.forEach(function (event) {
        pusher.trigger('email-events', 'new-event', {

Save the file and restart the server in the terminal.

Next, we need to expose our localhost server publicly using ngrok. Visit the ngrok website to find out how to set it up on your computer. Once you have it installed, start an HTTP tunnel on port 5000 by running the command below in a separate terminal window. You will be provided with a couple of URLs but you only need the last one which is HTTPS enabled.

    ./ngrok http 5000

Finally, let’s activate SendGrid’s event notification service. On the SendGrid dashboard, go to Settings > Mail Settings, and scroll to the Event Notification setting. Toggle the setting on, and click the edit link, then update the HTTP POST URL input with <your ngrok url>/events.

Following that, select the following actions: Processed, Dropped, Deferred, Delivered and Bounced, then click the blue checkmark on the right to save.

Not only will you be able to set up monitoring for your business critical emails, but you’ll be the first to know if anything goes wrong, allowing you to save time and money, and improve customer experience.

Test the application

Open the password reset form on http://localhost:3000 and the dashboard at http://localhost:3000/#/dash in a separate tab. Enter your email address and submit the form. After a short while, you should see the metrics update on the dashboard.

Wrap up

You have now learned how to create a realtime monitoring dashboard for transactional emails. It was easy wasn’t it? We covered a simple use case but you can take it further and set up monitoring for all your business's critical emails.

Thanks for reading! Remember that you can find the source code of this app in this GitHub repository.

reactjs react javascript

Bootstrap 5 Complete Course with Examples

Bootstrap 5 Tutorial - Bootstrap 5 Crash Course for Beginners

Nest.JS Tutorial for Beginners

Hello Vue 3: A First Look at Vue 3 and the Composition API

Building a simple Applications with Vue 3

Deno Crash Course: Explore Deno and Create a full REST API with Deno

How to Build a Real-time Chat App with Deno and WebSockets

Convert HTML to Markdown Online

HTML entity encoder decoder Online

How native is React Native? | React Native vs Native App Development

Article covers: How native is react native?, React Native vs (Ionic, Cordova), Similarities and difference between React Native and Native App Development.

Increase Performance of React Applications Via Array JavaScript Methods

Increase Performance of React Applications Via Array JavaScript Methods. We will create a simple event management application in the react to add, update, and delete an event.

Routing in React without React-Router

I have been using React JS in my projects for quite some time now and am used to managing routing in my app using the react-router package. I have always been keen on having as little dependencies in my apps as possible, so, I always felt perturbed by the use of this particular package in simpler apps which did not have complex routes.

Forms of Composition in JavaScript and React

One of the core ideas in functional programming is composition: building larger things from smaller things. The canonical example of this idea should be familiar with legos.

How to Setup React Router v5 using React Hooks

React Router library makes the navigation experience of the client in your web page application more joyful, but how?! React Router, indeed, prevent the page from being refreshed. Thus the blank page resulted from a refreshed page is not...