apps/docs/contributing/codebase-overview.mdx
We've created this guide to help new contributors understand and navigate the React Email codebase.
After cloning the React Email repository you will see a few root-level directories. Here's a brief overview of each:
<table> <thead> <tr> <th>Directory</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td> [apps](https://github.com/resend/react-email/tree/canary/apps) </td> <td> Here you can find all of the apps related to our online presence, like: - this documentation (under [apps/docs](https://github.com/resend/react-email/tree/canary/apps/docs)), - the demo emails we have on [demo.react.email](https://demo.react.email/preview/notifications/vercel-invite-user) (under [apps/demo](https://github.com/resend/react-email/tree/canary/apps/demo)) - the Next app we have for our landing page on [react.email](https://react.email) (under [apps/web](https://github.com/resend/react-email/tree/canary/apps/web)) </td> </tr> <tr> <td> [benchmarks](https://github.com/resend/react-email/tree/canary/benchmarks) </td> <td> We make benchmarks from version-to-version to demonstrate data-observable performance gains with metrics like *p99 and p75*. For example, see the [Improved Performance for Tailwind Emails](https://resend.com/blog/improved-performance-for-tailwind-emails) benchmark.
</td>
</tr>
<tr>
<td>
[packages](https://github.com/resend/react-email/tree/canary/packages)
</td>
<td>
Most contributions will be made to the packages in this directory.
This directory contains all our published [NPM](https://www.npmjs.com/) packages.
Each subdirectory is a single component published as its own package, with the exception of a few packages that serve as shared configuration.
</td>
</tr>
The react-email repository is a pnpm monorepo, which means it contains multiple packages.
Because we use pnpm, you will need to use pnpm to install and run each package. If you do not have pnpm installed, we recommend you install it using corepack:
corepack enable
corepack prepare pnpm@latest --activate
Currently, we have the following packages:
<div className="grid gap-2" style={{ gridTemplateColumns: '1fr 1fr 1fr' }}> <div> - [@react-email/body](https://github.com/resend/react-email/tree/canary/packages/body) - [@react-email/button](https://github.com/resend/react-email/tree/canary/packages/button) - [@react-email/code-block](https://github.com/resend/react-email/tree/canary/packages/code-block) - [@react-email/code-inline](https://github.com/resend/react-email/tree/canary/packages/code-inline) - [@react-email/column](https://github.com/resend/react-email/tree/canary/packages/column) - [@react-email/components](https://github.com/resend/react-email/tree/canary/packages/components) - [@react-email/container](https://github.com/resend/react-email/tree/canary/packages/container) - [create-email](https://github.com/resend/react-email/tree/canary/packages/create-email) - <span className="text-xs">Used for our [automatic setup](/getting-started/automatic-setup)</span> </div> <div> - [@react-email/font](https://github.com/resend/react-email/tree/canary/packages/font) - [@react-email/head](https://github.com/resend/react-email/tree/canary/packages/head) - [@react-email/heading](https://github.com/resend/react-email/tree/canary/packages/heading) - [@react-email/hr](https://github.com/resend/react-email/tree/canary/packages/hr) - [@react-email/html](https://github.com/resend/react-email/tree/canary/packages/html) - [@react-email/img](https://github.com/resend/react-email/tree/canary/packages/img) - [@react-email/link](https://github.com/resend/react-email/tree/canary/packages/link) - [@react-email/markdown](https://github.com/resend/react-email/tree/canary/packages/markdown) - [@react-email/preview](https://github.com/resend/react-email/tree/canary/packages/preview) </div> <div> - [react-email](https://github.com/resend/react-email/tree/canary/packages/react-email) - <span className="text-xs">The package for our [email CLI](/cli)</span> - [@react-email/preview-server](https://github.com/resend/react-email/tree/canary/packages/preview-server) - [@react-email/render](https://github.com/resend/react-email/tree/canary/packages/render) - [@react-email/row](https://github.com/resend/react-email/tree/canary/packages/row) - [@react-email/section](https://github.com/resend/react-email/tree/canary/packages/section) - [@react-email/tailwind](https://github.com/resend/react-email/tree/canary/packages/tailwind) - [@react-email/text](https://github.com/resend/react-email/tree/canary/packages/text) </div> </div>Most of these packages are very small and can be easily understood by reading the code, so feel free to explore.
We encourage using turborepo to manage the packages.
It's often helpful to install Turborepo globally to make it easier to run commands in any of the repositories. With a global installation, running turbo build in any of the packages will build both the package
you are on as well as the dependent packages. The global installation handles version mismatching as well.
The CLI is built using commander, a CLI builder for node. It handles parsing of command line arguments and ensuring that the syntax for the command is as expected.
The build, dev, and start commands all depend on the user first installing @react-email/preview-server.
Locally installing the preview server also includes all the required dependencies so you can run the necessary commands.
nypm and jiti work together to first ensure @react-email/preview-server is installed and then to import it into the CLI.
The Preview Server and the CLI work together. The CLI detects changes to files in the user's dependency graph with chokidar and then sends the updated files to the Preview Server using socket.io. Other details, like the path to the user's emails directory, are handled through environment variables.
The Preview Server is a Next.js app that uses esbuild at runtime to bundle the
user's email templates and then renders them using the render utility.
As changes from the CLI are passed through socket.io to the Preview Server, the preview updates automatically.
For testing, we use vitest. We prefer to define globals and run tests under the happy-dom environment.
We do not strictly enforce testing coverage, but encourage it.
For help testing, see our Development workflow guide.
<Note> The `@react-email/render` package's `renderAsync` does a fair bit of magic to simulate `edge` and other environments that are not supported by `happy-dom`. For this use case, we override the [environment on a per-file basis](https://vitest.dev/guide/environment#environments-for-specific-files) for its tests </Note>We use biomejs for linting and formatting. Both the linting and formatting are ensured by our GitHub CI so make sure you lint and format your code (pnpm lint:fix) before opening a PR or asking for a review on it.
For help linting and formatting, see our Development workflow guide on linting.
We use tsup to build most packages. (The only exception for this is the @react-email/tailwind package which currently uses vite due to a few issues with tsup and tailwindcss's bundling.) For help building packages, see our Development workflow guide.
We build most of the packages before publishing for a few reasons: