Welcome to the EpicWeb.dev Workshop app!

This is the deployed version. Run locally for full experience.

Email

Loading "Intro to Email"
Email is one of the most common ways to verify a user's identity. If they lose access to their account, need to reset their password, or want to change their email address, we send them a link to their email address.
The trouble with sending email is summed up in one word: deliverability. It's an unfortunate state of affairs, but if you're not using a service for email then you're probably not going to be able to send email reliably. Read more about this in this depressing blog post: After self-hosting my email for twenty-three years I have thrown in the towel. The oligopoly has won.. ๐Ÿ˜ข
So which service do you use? After evaluating many options, I have settled on Resend which has a great free tier for while you're getting started and then a very reasonable pricing model after that. Additionally, deliverability is excellent and they have a great API.
There is a standard for sending email and that's called SMTP. It's a bit of a pain to work with, so I recommend using a library to do it for you. A common library for Node.js applications is Nodemailer. The nice thing about using a service is it means you should be able to swap out your email provider without issue.
However, the problem with SMTP is that it's difficult to mock for local development. I prefer all my local development to work offline, so instead of using Nodemailer, I use the Resend REST API directly. It's simple and if you package things up well you shouldn't have any issue switching to a different provider anyway. I've actually done this (when I switched to Resend) and it didn't involve much work.
Another challenge with email is the limitation around styling and client support for various features. It's an enormous pain. However, Resend has worked on an incredibly useful library of React components and a custom React renderer so you can actually write your emails using React components! It even supports a custom tailwind config and converts those classes to styles supported by email clients. The library is react.email and you can find all the components via @react-email/components.
function PositiveAffirmationEmail({ name }: { name: string }) {
	return (
		<E.Html lang="en" dir="ltr">
			<E.Container>
				<h1>
					<E.Text>You are great, {name}</E.Text>
				</h1>
				<p>
					<E.Text>I just think you should know you are awesome.</E.Text>
				</p>
				<E.Link href="https://kcd.im/emoji">Here's my favorite emoji</E.Link>
			</E.Container>
		</E.Html>
	)
}

const [html, text] = await renderReactEmail(<PositiveAffirmationEmail />)
async function renderReactEmail(react: ReactElement) {
	const [html, text] = await Promise.all([
		renderAsync(react),
		renderAsync(react, { plainText: true }),
	])
	return { html, text }
}
Finally, it may be useful for you to get an idea of how the auth flow will work in this app:
auth flow showing communication between a user and server described below
Here's a description of the flow:
  • The user submits an email to the server
  • The server creates a verification in the database
  • The server redirects the user to a /verify route
  • The server sends the code to the user's email address
  • The user checks their email client for the code
  • The user submits the code
  • The server verifies the code with the verification in the database
  • The server redirects with a cookie
  • The user proceeds to onboard with a signed cookie identifying them as the owner of the email address their onboarding with.
And that's what you're going to build in this exercise. Let's go!