Sustainable Code Through Collaboration: React Email Bug Fixing
Introduction
Over the past few months, I had the opportunity to work closely with my team (Array of Sunshine) and mentor Piranavan Selvanandan to resolve a persistent bug within React Email. This experience taught me about package managers such as PNPM, symbolic linking of local files, careful testing, and applying effective problem-solving skills in a real-world context. In this post, I’ll share insights into the code we worked with, the challenges we faced when replicating the bug, and the final solution our team was able to produce.
What is React Email?
React Email is an email formatting and styling tool created by Resend. The tool uses React components and Typescript to beautify emails and fix formatting issues that plague the current email market. React Email is important because it addresses long-standing issues and outdated tools that email clients have failed to fix since 2010.
For example, if we take a small business that sells clothing online to customers around the globe, and that business wishes to send out emails to their customers promoting a brand-new item in stock, they may struggle to make sure their emails are formatted correctly. This is important not only for maintaining brand status but also for ensuring that the user is able to easily access and read the email. However, considering there are so many email platforms on the market currently (Outlook, Google, Yahoo, etc.), making sure that every email client formats the message correctly can be a major time waster and very irritating.
This is where React Email comes in and eases the issues of cross-platform integration by converting React components into exported HTML that can be read by all email platforms. This not only lessens the time the business must spend reformatting emails but also helps maintain consistent business identity. However, this is only one example of how React Email can improve the email-sending experience. This tool has become globally accessible to many businesses, both large and small, as well as to individuals who use it for everyday email sending and the numbers reflect its growing importance. According to npmcharts, React Email is currently sitting at over 1,600,000 npm downloads, with users leveraging this tool to enhance their email-sending experience.
HTML Formatting Issue
Our team was assigned to tackle GitHub issue #1767, titled “Incorrect display of quotes in styles and string links when exporting in React Email.” The poster of the issue mentioned that the bug stemmed from exporting HTML within the <style> tag, which resulted in an escape character (") being used when placing font family names in the style sheet.
For example, if the user wanted to specify a style for the <body> tag in React the user typically would write it as:
The correct formatting that suppose to have been produced within the code should have been:
Instead the program would reproduce escape characters around the quotes within the HTML Export:
Within the comments of this issue, our team noticed that the problem wasn’t limited to the style attribute. It was also affecting href links, specifically those containing the ampersand symbol (&). We also discovered that this style attribute issue didn’t only affect the <body> tag, it was impacting all other tags within the scope of our issue as well.
When tackling this problem head-on, our team decided to take the first steps in identifying the source of the bug. Our initial hypothesis was that the issue might stem from how the React component was being parsed into an HTML string. This led us to investigate the packages folder within React Email. Inside the code-base, we found several components and functions that support the program.
When we took a closer look at the packages, we discovered that the components served as templates for HTML tags, allowing React components to maintain their formatting and styling. However, they didn’t lead us to the point where the parsing was executed. Instead, we found that the HTML export functions were primarily contained within the React Email Render package, specifically in two render.tsx files.
These render.tsx files contained the code responsible for parsing the React components into HTML strings. The files also included React Email’s external package extensions: plain-to-text, which converts the HTML string into plain text by removing all HTML tags and focusing primarily on the content within the tags, and Prettier, a formatting tool that takes HTML strings and converts them into a more readable format. If neither extension is called, the program simply returns the HTML string as is.
Inside React-Email: Code Base Overview
Tech Stack
Flow of Control:
The course of action a user must take to export a React-Email template into an HTML string works as:
Challenge: Setting up a Testing Environment
We faced several challenges in determining how to start the process of recreating the bug and identifying where to focus within React Email. The first major hurdle was recreating the bug itself and understanding how React-Email works. To tackle this, our team divided the work, with each member investigating different approaches to recreate the bug and set up an environment where we could modify the code within React Email.
When trying to figure out how to recreate the bug and understand where to begin the debugging process, I broke the problem down into steps. My first step in problem-solving was to review React-Email’s documentation to learn how users implement the formatting package in their projects and how it’s used. I discovered that, in order to test the features within React-Email, I would first need to create a new project, download React-Email via NPM, import its components, and export the results to the browser console. With this in mind, I created our own testing environment, which can be found here.
Recommended by LinkedIn
While this solution helped us replicate the bug and find a way to test React-Email’s components, there was one issue: our current import of React-Email wouldn't reflect any changes made in our local forked repository. The only way we would have been able to see our changes was by making a pull request to React-Email, which obviously wouldn't work for our purposes. This led to our second challenge: how could we implement the changes we were working on and export them to our testing environment?
Connecting repositories was something I had never personally worked with before. However, after some research, I discovered that the solution to the problem was to use symbolic linking by connecting my local files with each other using npm link. In my terminal, I navigated to each of my files and created a global link between the two, hoping this would apply the changes from one file to our testing environment. Unfortunately, this approach led me to a roadblock. Despite successfully creating a global link, the changes I was making to React-Email weren't automatically reflected in the testing environment as I had hoped.
Due to my inexperience with symbolic linking, I found myself at a roadblock, having exhausted all my options, and ultimately had to ask for help. Our mentor, Piranavan, made himself available for one-on-one sessions to assist us when we got stuck. He helped me understand why the issue was happening and guided me through the proper way to link the repositories. We worked together, testing and troubleshooting the linking problem. Eventually, Piranavan found a solution that fixed the connection issue. However, the process was slow, and he recommended that our team create a testing environment within our forked repository to streamline future testing.
Sadly, due to time constraints, we decided to continue with the current state of the testing environment. However, for future bug fixes and iterations, I believe it would be best to create a new testing environment within React-Email. As a result, I plan to implement these changes for the next bug fix.
Our Solution
After our initial struggle with fixing the testing environment, our team quickly shifted focus to finding a possible solution to the problem. We found one potential fix using an encoding package known as HE. While HE successfully resolved our original escape character issue, it introduced a few complications. These complications entailed:
We decided to take a step back and think about why this fix wasn’t working as intended. After reviewing the code in our testing environment, we noticed that while escape characters like " were being decoded into their corresponding characters, the resulting HTML still had formatting errors. For example, the correct formatting for this code should be:
<body style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif"></body>
However our code (implemented with HE) was producing:
<body style="font-family: "Helvetica Neue", Helvetica, Arial, sans-serif"></body>
This incorrect HTML formatting prevented the font from being implemented as intended. Based on this finding, we explored different approaches to ensure the exported HTML followed the correct formatting for the style tag. During my research to find an algorithm or function to replace these character quotes, I discovered a way to implement a check within the style and href tags using regex.
In my research, I learned that regex is a tool used to search, match, and manipulate text based on specific patterns. It consists of a sequence of characters that define a search pattern. These patterns can be used to find text, validate input, or replace text in strings. Given that our bug was caused by incorrect formatting from characters, I considered adding a check within the Render package—specifically in the render.tsx component, located in both the Node and Browser folders. If this check can detect escape characters, it can prevent these unwanted escape sequences or incorrect characters from passing through into the final HTML export.
Our solution can be seen here:
You can see how the regex function was integrated into our project. In lines 1-3, we've added an html.search() statement to check for each escape character symbol that appears within the attributes. If an escape character is found in the style or href attributes, it is assigned a true value (1) or if no escape characters are found then it is given a false value (0). When double or single quote escape characters are detected, they are replaced with single quotes (‘). If an ampersand escape is found in the href attribute, the escape characters are replaced with the ampersand symbol (&). If none of these characters are found, the regex functions are ignored, and the HTML is exported as-is. After we implemented our solution we created a pull request to be reviewed by Resend. The pull request included in this document can be found here.
To ensure our solution was correct, our team used the testing environment we created earlier to examine the exported HTML produced in the console. To visualize all the possible tags that could be used by a user, we updated our testing component to include every potential React-Component tag. Using this component, we were able to compare the outputs of the style and href attributes. We also inserted temporary console.log() statements throughout our code in React-Email to check if our solution successfully detects any escape characters within the tags.
Additionally, we created a testing table and recorded our results whether the program passed or failed the tests. This tests that we preformed checked our results before implementing our regex code with and without prettier enabled and after our solution was implemented. The reason our team tested our solution with Prettier is because we wanted to ensure that our solution did not cause any issues with the formatting extension enabled.
As shown in the screenshot above, with our solution implemented, we successfully replaced the escape characters with single quotes. Our solution also resolves the initial HTML tag formatting issue by replacing the incorrect quotation marks with the correct symbols. Therefore showing us that our regex solution seems to have fixed our bug and with our testing the check is passing throughout all components.