🔥 Building an email automation system with React Flow and Resend 🎉
Tags: webdev, javascript, tutorial, react
Published: 2023-07-31 12:44:48

# TL;DR

In this tutorial, you'll learn how to build an email automation system to message people with a sequence of messages every 10 minutes. ⏰

- Build a client diagram representing the flow of emails with React Flow. ⿳
- Send email according to the flow every 10 minutes with Resend. 📝


![Email](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u2650hazu8lmg4g2u7vn.gif)

<hr />

## Novu - the first open-source notification infrastructure
Just a quick background about us. Novu is an open-source [notification infrastructure](https://novu.co). We basically help to manage all the product notifications. It can be **In-App** (the bell icon like you have in the Dev Community - **Websockets**), Emails, SMSs and so on.

**We actually implemented ReactFlow and Resend in our project as well**

I would be super happy if you could give us a star! It will help me to make more articles every week 🚀
https://github.com/novuhq/novu

![Novu](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yn90yvsd87tgik03c08v.gif)

<hr />

## ReactFlow to build your flow ✅

[ReactFlow](https://reactflow.dev/) is an easy-to-use library for building anything from static diagrams to data visualizations and even complex visual editors. It is highly customizable and provides various in-built features such as dragging nodes around, zooming and panning, selecting multiple nodes and edges, and many more by default.

In this article, you'll learn how to add interactive diagrams to your React apps with ReactFlow and how to send emails with [Resend](https://resend.com/) by building an email outreach application.

The application accepts various email content via nodes in ReactFlow and sends them as email messages.

![ReactFlow](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pw5wlcoivpn6dqvhfia6.gif)

<hr />

## Let's set it up 🔥

Here, I'll walk you through installing the package dependencies required for this project; using Next.js v12.

```bash
npx create-next-app@12 email-outreach-app
```

Run the code snippet below to install the ReactFlow and Resend packages.

```bash
npm install reactflow resend
```

Finally, install [React Redux](https://react-redux.js.org/) and [Redux Toolkit](https://redux-toolkit.js.org/) packages to enable us to manage states within the application.

```bash
npm install react-redux @reduxjs/toolkit
```

## Putting the basic page layout 📟

Here, we'll create a form that accepts an email, a subject, and a series of nodes containing the messages you want to send to the recipient. The messages will be sent at an interval of 30 minutes.

First, copy the code snippet below into the `pages/index.js` file.

```jsx
import Head from "next/head";
import { useState } from "react";

export default function Home() {
	const [email, setEmail] = useState("");
	const [subject, setSubject] = useState("");

	const handleSubmit = (e) => {
		e.preventDefault();
		console.log({ email, subject });
		setEmail("");
		setSubject("");
	};

	return (
		<>
			<Head>
				<title>Email Outreach - Resend & ReactFlow</title>
				<meta name='description' content='Generated by create next app' />
				<meta name='viewport' content='width=device-width, initial-scale=1' />
				<link rel='icon' href='/favicon.ico' />
			</Head>
			<main className='main'>
				<header className='header'>
					<h1 style={{ marginBottom: "15px" }}>
						Email Outreach with ReactFlow and Resend
					</h1>
				</header>

				<form className='form' onSubmit={handleSubmit}>
					<label htmlFor='email'>Email</label>
					<input
						type='email'
						name='email'
						id='email'
						className='input'
						value={email}
						required
						onChange={(e) => setEmail(e.target.value)}
					/>

					<label htmlFor='subject'>Subject</label>
					<input
						type='text'
						name='subject'
						id='subject'
						className='input'
						value={subject}
						required
						onChange={(e) => setSubject(e.target.value)}
					/>
					{/* --- 👉🏻 ReactFlow Component placeholder 👈🏼 --- */}
					<button className='submitBtn'>START AUTOMATION</button>
				</form>
			</main>
		</>
	);
}
```

The code snippet above creates a simple form that accepts the recipient's email address and the subject of the email. In the upcoming section, we'll add the ReactFlow component.

### Managing states within the ReactFlow components

Before you import the ReactFlow components, let's set up the state management library - Redux Toolkit.

> **💡 PS: You don't need a state management library to use ReactFlow.**
> 

We are using Redux to enable us to track the input within the component and update the application's state accordingly. Otherwise, [you can add ReactFlow components easily](https://reactflow.dev/docs/quickstart).

Therefore, create a `redux` folder containing a `nodes.js` and a `store.js` file.

```bash
mkdir redux
cd redux
touch nodes.js store.js
```

Copy the code snippet below into the `redux/nodes.js` file.

```jsx
import { createSlice } from "@reduxjs/toolkit";

const addNode = (object) => {
	const newNode = {
		id: `${Number(object.id) + 1}`,
		type: "task",
		position: { x: 0, y: object.position.y + 120 },
		data: { value: "" },
	};
	return newNode;
};

const addEdge = (object) => {
	const newEdge = {
		id: `${object.id}->${Number(object.id) + 1}`,
		source: `${object.id}`,
		target: `${Number(object.id) + 1}`,
	};
	return newEdge;
};
```

The code snippet above contains two functions that accept an object (the last element in the nodes array) and returns another object containing the values above.

Next, add the code snippet below the functions - in the same file.

```jsx
//below the functions (within the same file)
//---- 👉🏻 functions 👈🏼---

export const nodeSlice = createSlice({
	name: "nodes",
	initialState: {
		nodes: [
			{
				id: "1",
				type: "task",
				position: { x: 0, y: 0 },
				data: { value: "" },
			},
		],
		edges: [],
	},
	reducers: {
		setNodes: (state, action) => {
			let nodes = state.nodes;
			state.nodes = [...state.nodes, addNode(nodes[nodes.length - 1])];
			state.edges = [...state.edges, addEdge(nodes[nodes.length - 1])];
		},
		updateNodeValue: (state, action) => {
			let nodes = [...state.nodes];
			let objectIndex = nodes.findIndex((obj) => obj.id === action.payload.id);
			if (objectIndex !== -1) {
				state.nodes[objectIndex] = {
					...nodes[objectIndex],
					data: { value: action.payload.value },
				};
			}
		},
	},
});

// Action creators are generated for each case reducer function
export const { setNodes, updateNodeValue } = nodeSlice.actions;

export default nodeSlice.reducer;
```

- From the code snippet above,
    - We created two states - `nodes` and `edges` arrays. The `nodes` state has a single element representing the initial node in the diagram.
    - The `setNodes` reducer updates the `nodes` and `edges` array. It executes when the user clicks the `Add button` within each diagram node.
    - The `updateNodeValue` reducer tracks the input within each node of the diagram and updates the right node with its new value.
    

![Resize](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zu7awh25bxpdj97zr7l6.gif)

Add the node reducer to the `store.js` file.

```jsx
import { configureStore } from "@reduxjs/toolkit";
import nodeReducer from "./nodes";

export const store = configureStore({
	reducer: {
		nodes: nodeReducer,
	},
});
```

Finally, make the store available to the whole application by updating the `_app.js` file.

```jsx
import { store } from "../redux/store";
import "../styles/globals.css";
import { Provider } from "react-redux";

export default function App({ Component, pageProps }) {
	return (
		<Provider store={store}>
			<Component {...pageProps} />
		</Provider>
	);
}
```

Congratulations! You've set up the states required for the diagram. Next, let's add it to the app.

### Adding the ReactFlow components

Since we are using a custom component for each node in the diagram, create a `components` folder containing a `Task.js` file.

```bash
mkdir components
cd components
touch Task.js
```

Copy the code below into the `Task.js` file. The Task component represents each node in the diagram.

```jsx
import { useState } from "react";
import { Handle, Position } from "reactflow";
import { useSelector, useDispatch } from "react-redux";
import { setNodes, updateNodeValue } from "../redux/nodes";

export default function Task({ id }) {
	const initialNodes = useSelector((state) => state.nodes.nodes);
	const [value, setValue] = useState("");
	const dispatch = useDispatch();

	return (
		<>
			<Handle type='target' position={Position.Top} />
			<div
				style={{
					padding: "10px",
					backgroundColor: "#F5F5F5",
					borderRadius: "5px",
				}}
			>
				<input
					className='textInput'
					type='text'
					required
					onChange={(e) => {
						setValue(e.target.value);
						dispatch(updateNodeValue({ id, value: e.target.value }));
					}}
					value={value}
				/>
				{Number(id) === initialNodes.length && (
					<button onClick={() => dispatch(setNodes())} className='addBtn'>
						ADD NODE
					</button>
				)}
			</div>

			<Handle type='source' position={Position.Bottom} id='a' />
		</>
	);
}
```

- From the code snippet above,
    - The [Handle components](https://reactflow.dev/docs/api/nodes/handle) rendered at the top and bottom connect each node to another. It has a type prop that determines whether the node is a source or target.
    - The `Add Node` button triggers the `setNodes` reducer.
    - When a user updates the content within the input field, the `updateNodeValue` reducer is also triggered to update the selected note with the input value.
    - Each node in the diagram has a `data` and an `id` props containing the details of that node.

Next, add the following imports to the `pages/index.js` file.

```jsx
import { useState, useCallback, useMemo, useEffect } from "react";
import ReactFlow, {
	useNodesState,
	useEdgesState,
	getIncomers,
	getOutgoers,
	addEdge,
	getConnectedEdges,
} from "reactflow";
import "reactflow/dist/style.css";
import Task from "../components/Task";
import { useSelector } from "react-redux";
```

Add the code snippet below within the Home component on the `pages/index.js` file.

```jsx
const initialNodes = useSelector((state) => state.nodes.nodes);
const initialEdges = useSelector((state) => state.nodes.edges);
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
const nodeTypes = useMemo(() => ({ task: Task }), []);

useEffect(() => {
	setNodes(initialNodes);
	setEdges(initialEdges);
}, [initialNodes, setNodes, initialEdges, setEdges]);

const onConnect = useCallback(
	(params) => setEdges((eds) => addEdge(params, eds)),
	[setEdges]
);
```

- From the code snippet above,
    - The nodes and edges from the Redux state are set as the nodes and edges for the diagram using the `useNodesState` and `useEdgesState` hooks provided by [ReactFlow](https://reactflow.dev/docs/api/hooks/use-nodes-state/).
    - The `nodeTypes` variable enables us to customise each node. `Task` is our custom component.
    - The `onConnect` function executes when you add a new node.
    - The `useEffect` hook runs when there are changes in the edges and the nodes.

Finally, add the `ReactFlow` component to the user interface as done below.

```jsx
return (
	<form>
		{/*---👉🏻 other form elements 👈🏼---*/}
		<div style={{ height: "60vh", width: "100%", marginTop: "20px" }}>
			<ReactFlow
				nodes={nodes}
				edges={edges}
				onNodesChange={onNodesChange}
				onEdgesChange={onEdgesChange}
				onConnect={onConnect}
				nodeTypes={nodeTypes}
			/>
		</div>
		<button className='submitBtn'>START AUTOMATION</button>
	</form>
);
```

Congratulations, you've successfully added the diagram to your application.

<hr />

## Resend.com to send your emails 📜

In this section, you'll learn how to send emails with Resend by sending the inputs in each node to the email provided on the form.

[Resend](https://resend.com/) is an email API that enables you to send texts, attachments, and email templates easily. With Resend, you can build, test, and deliver transactional emails at scale.

One of its best features is that your messages don't end up in the recipient's spam box but in the recipient's inbox.

We've already installed Resend at the beginning of this tutorial. Therefore, go to the [Signup page and create an account](https://resend.com/signup).


![Resend](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h90i3t5s13byudn0fcn5.png)

Create an API Key and save it into a `.env.local` file within your Next.js project.

```
RESEND_API_KEY=<place_your_API_key>
```

Next, create a `send.js` file within the `pages/api` folder and copy the code below into the file.

```jsx
//👇🏻 within the send.js file
import { Resend } from "resend";

// initiate the resend instance
const resend = new Resend(process.env.RESEND_API_KEY);

const timer = (time) => {
	return new Promise((res) => {
		setTimeout(() => res(true), time);
	});
}

export default async function handler(req, res) {
	const { subject, email, tasks } = req.body;
	if (!subject || !tasks || !email) {
		res.status(400).json({invalid: true});
	}

	for (const task of tasks) {
		await resend.emails.send({
			from: "name@yourcompany.dev",
			to: [email],
			subject,
			text: task,
		});
		
		// Wait 10 minutes
		await timer(600000);
	}

	res.status(200).json({invalid: false});
}
```

The code snippet above receives the subject, recipient, and email content from the request and sends an email to the recipient via [Resend](https://resend.com/docs/send-with-nextjs).

Please be advice that there is a delay of 10 minutes between emails.

This will not be possible to be deployed to Vercel as their free package support a maximum of 10 seconds per request.

You can absolutly test it on your local machine.

In production, such a thing would need to go into a queue that sends the email every X amount of time.

Add the following functions within the `pages/index.js` file.

```jsx

const sendEmail = (index) => {
	fetch("/api/send", {
		method: "POST",
		body: JSON.stringify({
			email,
			subject,
			tasks: nodes.map(data => data.value), // map all nodes to a string array
		}),
		headers: {
			"Content-Type": "application/json",
		},
	})
		.then((data) => {
			alert(`Sent to processing`);
		})
		.catch((err) => {
			alert(`Encountered an error when message${index} ❌`);
			console.error(err);
		});
};
```

The functions above loop through the nodes in the ReactFlow diagram and sends an email containing the node's value to the recipient at intervals.

Finally, execute the function when a user submits the form.

```jsx
const handleSubmit = (e) => {
	e.preventDefault();
	sendEmail();//👈🏼 Send to server
	setEmail(""); // Reset the input
	setSubject(""); // Reset the input
};
```

<hr />

## Let's wrap it up 🎁

So far, you've learned how to add interactive diagrams to your application with ReactFlow and send emails with Resend.

ReactFlow is a [popular open-source library](https://github.com/wbkd/react-flow) that enables us to build interactive and customizable flowcharts and diagrams. If you want to build an application that requires drag-and-drop functionality and customizable graphical UI elements, you should consider using ReactFlow.

The source code for this tutorial is available here:  https://github.com/novuhq/blog/tree/main/email-outreach-with-reactflow-and-resend

Thank you for reading! 🎉

<hr />

## Help me out!
If you feel like this article helped you understand email automation better! I would be super happy if you could give us a star! And let me also know in the comments ❤️
https://github.com/novuhq/novu


![Help](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9fzew5x2g7tpfl7jqubq.gif)