How To Build Your Own Newsletter App? p.2
This is a guide on how to build a newsletter application that allows users to subscribe to a mailing list using a Google or GitHub account. It uses Next.js, Firebase, and Novu. Part 2.
Sending newsletters with Novu and React Email
We’ll leverage Novu’s code-first workflow (Echo) approach in creating notification workflows within our codebase. Novu empowers you with the ability to integrate email, SMS, and chat template and content generators, such as React Email, and MJML into your app via code.
Within this section, you’ll grasp the art of crafting notification workflows using Novu Echo, designing email templates with React Email, and seamlessly sending emails via Novu.
First, you need to create an account on Novu and set up a primary email provider. We’ll use Resend for this tutorial.
After creating an account on Novu, create a Resend account, and select API Keys
from the sidebar menu on your dashboard to create one.
Next, return to your Novu dashboard, select Integrations Store
from the sidebar menu, and add Resend as an email provider. You’ll need to paste your Resend API key and email address into the required fields.
Finally, select Settings
from the sidebar menu and copy your Novu API key into a .env.local
file as shown below.
# For Novu SDK
NOVU_API_KEY=<YOUR_NOVU_API_KEY>
# For Echo Client
NEXT_PUBLIC_NOVU_API_KEY=<YOUR_NOVU_API_KEY>
The API route for creating newsletters
Navigate to the components/Newsletters.tsx
file, which contains the form that enables Admin users to send newsletters to subscribers. Copy the functions below into this file.
const [subscribers, setSubscribers] = useState<SubscribersData[]>([]);
//👇🏻 fetch the subscribers' emails from Firebase
const fetchSubscribers = async () => {
try {
const querySnapshot = await getDocs(collection(db, "subscribers"));
const emails: string[] = [];
const subs: any = [];
querySnapshot.forEach((doc) => {
const dt = doc.data();
emails.push(dt.email);
subs.push(dt);
});
setSubscribers(subs);
} catch (e) {
console.error("Error fetching subscribers", e);
}
};
//👇🏻 gets the list of subscribers on page load
useEffect(() => {
fetchSubscribers();
}, []);
//👇🏻 send form data to the backend API route
const emailNewsletter = async () => {
try {
const request = await fetch("/api/send", {
method: "POST",
body: JSON.stringify({ subject, message, subscribers }),
});
const response = await request.json();
if (response.success) {
console.log({ response });
}
} catch (err) {
console.error(err);
}
};
The provided code snippet retrieves the list of subscribers from Firebase and sends them, along with the email’s message and subject, to the /api/send
API route, where the emails are forwarded to the subscribers via Novu.
The fetchSubscribers
function retrieves the subscribers from Firebase, while the emailNewsletter
function sends a POST request containing the subscribers’ data, email subject, and content to the api/send
Next.js API route.
To proceed, let’s set up the required API routes. Create an api/send
folder within the Next.js app
folder.
cd app
mkdir api && cd api
mkdir send
Create a route.ts
file within the send
folder that accepts the form data.
import { NextRequest, NextResponse } from "next/server";
export async function POST(req: NextRequest) {
const { subject, message, subscribers } = await req.json();
return NextResponse.json(
{
message: "Data received!",
data: {subject, message: subscribers}
success: true,
},
{ status: 200 }
);
}
Now, we can use this data to send email templates using Novu Echo and React Email.
Code-first email notification workflow with Novu and React Email
Run this code snippet in your terminal to install the Novu SDK and Novu Echo to the Next.js project. We’ll create the notification workflow with Novu Echo and pass a payload from the API server to the workflow, then into the React Email template.
npm install @novu/node @novu/echo
Next, install React Email by running the following command:
npm install react-email @react-email/components -E
Include the following script in your package.json
file. The --dir
flag gives React Email access to the email templates located within the project. In this case, the email templates are located in the src/emails
folder.
{
"scripts": {
"email": "email dev --dir src/emails"
}
}
Run npm run email
in your terminal. This command starts the React Email development server, allowing you to choose from the existing templates. Alternatively, you can create an emails
folder containing an index.tsx
file and copy this email template into it.
import {
Body,
Column,
Container,
Head,
Heading,
Hr,
Html,
Link,
Preview,
Section,
Text,
Row,
} from "@react-email/components";
import * as React from "react";
const EmailTemplate = () => (
<Html>
<Head />
<Preview>Hello World</Preview>
<Body style={main}>
<Container style={container}>
<Section style={header}>
<Row>
<Column style={headerContent}>
<Heading style={headerContentTitle}>Hello World</Heading>
</Column>
</Row>
</Section>
<Section style={content}>
<Text style={paragraph}>Hey Emil,</Text>
<Text style={paragraph}>Cool</Text>
</Section>
</Container>
<Section style={footer}>
<Text style={footerText}>
You're receiving this email because your subscribed to Newsletter
App
</Text>
<Hr style={footerDivider} />
<Text style={footerAddress}>
<strong>Newsletter App</strong>, ©{" "}
<Link href='<https://novu.co>'>Novu</Link>
</Text>
</Section>
</Body>
</Html>
);
export default EmailTemplate;
const main = {
backgroundColor: "#f3f3f5",
fontFamily: "HelveticaNeue,Helvetica,Arial,sans-serif",
};
const headerContent = { padding: "20px 30px 15px" };
const headerContentTitle = {
color: "#fff",
fontSize: "27px",
fontWeight: "bold",
lineHeight: "27px",
};
const paragraph = {
fontSize: "15px",
lineHeight: "21px",
color: "#3c3f44",
};
const divider = {
margin: "30px 0",
};
const container = {
width: "680px",
maxWidth: "100%",
margin: "0 auto",
backgroundColor: "#ffffff",
};
const footer = {
width: "680px",
maxWidth: "100%",
margin: "32px auto 0 auto",
padding: "0 30px",
};
const content = {
padding: "30px 30px 40px 30px",
};
const header = {
borderRadius: "5px 5px 0 0",
display: "flex",
flexDireciont: "column",
backgroundColor: "#2b2d6e",
};
const footerDivider = {
...divider,
borderColor: "#d6d8db",
};
const footerText = {
fontSize: "12px",
lineHeight: "15px",
color: "#9199a1",
margin: "0",
};
const footerLink = {
display: "inline-block",
color: "#9199a1",
textDecoration: "underline",
fontSize: "12px",
marginRight: "10px",
marginBottom: "0",
marginTop: "8px",
};
const footerAddress = {
margin: "4px 0",
fontSize: "12px",
lineHeight: "15px",
color: "#9199a1",
};
At this point, you should have an email template ready to integrate into Novu Echo. 🚀
To get started with Novu Echo, close the React Email server, and run the code snippet below. It opens the Novu Dev Studio in your browser.
npx novu-labs@latest echo
Create an echo
folder containing a client.ts
file within the Next.js app folder and copy this code snippet into the file.
import { Echo } from "@novu/echo";
import { renderEmail } from "@/emails";
interface EchoProps {
step: any;
payload: {
subject: string;
message: string;
firstName: string;
};
}
export const echo = new Echo({
apiKey: process.env.NEXT_PUBLIC_NOVU_API_KEY!,
devModeBypassAuthentication: process.env.NODE_ENV === "development",
});
echo.workflow(
"newsletter",
async ({ step, payload }: EchoProps) => {
await step.email(
"send-email",
async () => {
return {
subject: `${payload ? payload?.subject : "No Subject"}`,
body: renderEmail(payload),
};
},
{
inputSchema: {
type: "object",
properties: {},
},
}
);
},
{
payloadSchema: {
type: "object",
properties: {
message: {
type: "string",
default: "This is a test message from Newsletter",
},
subject: { type: "string", default: "Message from Newsletter App" },
firstName: { type: "string", default: "User" },
},
required: ["message", "subject", "firstName"],
additionalProperties: false,
},
}
);
The code snippet defines a Novu notification workflow named newsletter
, which accepts a payload
containing the email subject, message, and the recipient’s first name. This payload contains the necessary data for the React Email template.
Update the React Email template to receive and display the payload data:
const EmailTemplate = ({
message,
subject,
firstName,
}: {
message: string;
subject: string;
firstName: string;
}) => (
<Html>
<Head />
<Preview>{subject}</Preview>
<Body style={main}>
<Container style={container}>
<Section style={header}>
<Row>
<Column style={headerContent}>
<Heading style={headerContentTitle}>{subject}</Heading>
</Column>
</Row>
</Section>
<Section style={content}>
<Text style={paragraph}>Hey {firstName},</Text>
<Text style={paragraph}>{message}</Text>
</Section>
</Container>
<Section style={footer}>
<Text style={footerText}>
You're receiving this email because your subscribed to Newsletter
App
</Text>
<Hr style={footerDivider} />
<Text style={footerAddress}>
<strong>Newsletter App</strong>, ©{" "}
<Link href='<https://novu.co>'>Novu</Link>
</Text>
</Section>
</Body>
</Html>
);
//👇🏻 passes the payload as props into the component
export function renderEmail(inputs: {
message: string;
subject: string;
firstName: string;
}) {
return render(<EmailTemplate {...inputs} />);
}
The <EmailTemplate/>
component accepts the email’s message, subject, and recipient’s first name as props to replace the existing variables within the component.
Next, you need to create an API route for Novu Echo. Within the api
folder, create an email
folder containing a route.ts
file and copy the provided code snippet below into the file.
import { serve } from "@novu/echo/next";
import { echo } from "../../echo/client";
export const { GET, POST, PUT } = serve({ client: echo });
Make sure you have run npx novu-labs@latest echo in your terminal. This command will automatically open the Novu Dev Studio at this URL: http://localhost:2022/ in your browser. Here, you can select the exact API endpoint you served Novu Echo and preview your email templates before adding to the Novu Cloud Platform.
Once you can preview your email template within the Novu Dev Studio, you can save the workflow to your Novu dashboard. Click the Sync to Cloud
button at the top right side of the page.
The Sync to Cloud
button triggers a pop-up that provides instructions on how to push your workflow to the Novu Cloud.
To proceed, run the following code snippet in your terminal. This will generate a unique URL representing a local tunnel between your development environment and the cloud environment.
npx localtunnel --port 3000
Copy the generated link along with your Echo API endpoint into the Echo Endpoint
field and then click the Create Diff
button.
https://<LOCAL_TUNNEL_URL>/<ECHO_API_ENDPOINT>
Afterward, scroll down and select Deploy Changes
to push the workflow to the cloud environment.
You can also preview your workflow on the Novu Cloud by clicking the Test your workflows
link from the notification alert. Congratulations! Your workflow has been successfully deployed.
Now, you can start sending emails via the workflow. Open the api/send
API route and copy the provided code snippet below into the route.ts
file.
import { NextRequest, NextResponse } from "next/server";
import { SubscribersData } from "../../util";
import { Novu } from "@novu/node";
//👇🏻 Novu SDK for sending emails
const novu = new Novu(process.env.NOVU_API_KEY!);
export async function POST(req: NextRequest) {
const { subject, message, subscribers } = await req.json();
subscribers.forEach(async (subscriber: SubscribersData) => {
const { data } = await novu.trigger("newsletter", {
to: {
subscriberId: subscriber.subscriberId,
email: subscriber.email,
firstName: subscriber.firstName,
lastName: subscriber.lastName,
},
payload: {
subject,
message,
firstName: subscriber.firstName,
},
});
console.log(data.data);
});
return NextResponse.json(
{
message: "Emails sent successfully",
success: true,
},
{ status: 200 }
);
}
The code snippet uses the Novu SDK to send an email to all the subscribers within the application.
Congratulations! 🎉You’ve completed the project for this tutorial.
Conclusion
So far, you’ve learnt how to implement multiple authentication methods with Firebase, store and retrieve data from Firebase, create email templates with React Email, and send them via Novu.
You also learned how to leverage the code-first notification workflow approach to build notification workflows within the code while giving your non-technical teammates full control over content and behaviour.
Novu is your best choice to add various notification channels to your applications, including Chat, SMS, Email, Push, and In-app notifications.
The source code for this tutorial is available here: https://github.com/novuhq/newsletter-app-with-novu-echo
Thank you for reading!