Writing Email Templates
How to build professional email templates using React components, what every template must include, and the standard EduTrack email set.
Email HTML is not the same as web HTML. An email needs to look correct in Gmail on a phone, Outlook 2016 on a corporate laptop, Apple Mail on a Mac, and Yahoo Mail in a browser — all at the same time. These clients support different subsets of HTML and CSS, and they actively strip out or ignore things like <script> tags and many CSS properties.
Writing email HTML by hand is painful. The solution is React Email — a library that provides pre-built components designed specifically for email compatibility, and lets you write your templates using React and TypeScript. The same skill.
The Excel Analogy
Think of email templates as Excel report templates.
You have a standard financial report layout — company logo at the top, client name and date in the header, data in the middle, totals at the bottom, footer with contact details. You fill in the data fields for each client and print 50 reports.
An email template works the same way. You design the layout once — logo, greeting, content area, call-to-action button, footer. Then you pass different data for each send: { parentName, studentName, feeName, receiptId }. The template generates a professional email for each event automatically.
Two Approaches: HTML Strings vs React Email
Approach 1: HTML Strings (Simple, for quick emails)
For simple emails with minimal formatting, you can write HTML directly in your edge function:
When to use this: Training projects, quick confirmation emails, internal notifications. Fine for development.
Limitation: It becomes messy and hard to maintain as emails get more complex. Inline styles for everything. No type safety. No component reuse.
Approach 2: React Email (Professional, for production)
For production applications, write email templates as React components using the @react-email/components library. This is the approach used in EduTrack.
Setting Up React Email in Edge Functions
In your Supabase edge function, you import both the render function and React Email components:
These components handle the messy cross-email-client compatibility for you. <Button> renders as an HTML table-based button (not a <button> tag) because many email clients don't support <button>. <Container> adds correct center-alignment that works in Outlook. You write clean React — the library handles the ugly internals.
A Complete Template Example
Here is the fee receipt email for EduTrack:
Using This Template in an Edge Function
The render() function converts your React component into an email-compatible HTML string. That string goes directly into resend.emails.send as the html field.
The Standard EduTrack Email Set
Six templates cover all the email communication EduTrack needs. Each one is a separate .tsx file in supabase/functions/_shared/templates/.
| Template File | Subject | Recipient | Trigger |
|---|---|---|---|
FeeReceiptEmail.tsx | "Fee paid — Receipt #[ID]" | Parent | Fee payment verified |
FeeReminderEmail.tsx | "Fee overdue — [Fee Name] for [Student]" | Parent | Fee past due date |
EnrollmentConfirmationEmail.tsx | "Enrollment confirmed — [Student] at [Branch]" | Parent | Student enrolled |
ResultPublishedEmail.tsx | "Exam results available — [Student]" | Parent | Results entered by teacher |
BranchAdminWelcomeEmail.tsx | "Welcome to EduTrack — [Branch Name] is ready" | Branch Admin | Branch created by super admin |
AnnouncementEmail.tsx | "[Branch]: [Announcement Title]" | Parents in branch | Announcement posted |
What Every Template Must Include
Regardless of the email type, every template should have these elements:
1. Logo — Top center. Builds instant brand recognition. Use an absolute URL to your logo hosted on your domain (email clients don't load relative URLs).
2. Clear heading — What this email is about, in 3–5 words. The user should know the email's purpose before reading anything else.
3. Personalization — Use the recipient's name. "Hi Priya" feels different from "Hi there." The data is already in your database.
4. The key information — The specific details relevant to this event. For a fee payment confirmation: date, amount, fee name, receipt ID. Keep it scannable — use a details box, not paragraph text.
5. A clear next action (CTA button) — Where should the user go next? "View Receipt", "Complete Profile", "Download Receipt." One button per email.
6. What to do if something is wrong — A line about contacting support or taking a corrective action. Reduces support tickets.
7. Footer — Company name, address (legally required in many countries for commercial email), support email, unsubscribe link.
The Unsubscribe Link
The unsubscribe link is not optional. In India, the TRAI regulations on commercial communications and internationally the CAN-SPAM Act and GDPR all require a mechanism to opt out of commercial email. For transactional email (fee payment confirmations, payment receipts) it is considered good practice even where not strictly legally required.
For EduTrack, the simplest implementation:
- A
notification_preferencestable in Supabase withuser_idandfee_emails: boolean,result_emails: boolean,announcement_emails: boolean - A Supabase Edge Function at
/unsubscribe?token=xxxthat reads the token, identifies the user, and sets the relevant preference tofalse - The unsubscribe URL in every email is this edge function URL with a signed token unique to the user
This is built in the Backend module — for now, include a placeholder {unsubscribeUrl} in your templates and connect it later.
Why Email Styles Are Inline Objects, Not Tailwind
You may notice that email templates use JavaScript objects for styles (like style={headingStyle}) rather than Tailwind class names (like className="text-2xl font-bold").
Email clients strip CSS classes. Gmail, Outlook, and Yahoo Mail do not load external stylesheets. Many of them also strip <style> tags in the <head>. Only inline styles survive reliably across all email clients.
Tailwind generates CSS classes in a stylesheet. Those classes work in browsers — they do not work in email clients. When you build email templates, inline styles are the correct approach, not a limitation to work around.
React Email's components handle most of the compatibility work — you just need to write the style objects.
Testing Templates Before Sending
The @react-email/render package can also be used locally to preview your email as HTML. For Supabase Edge Functions, the fastest testing approach is:
- Send a test email to a real address you control (your own Gmail account)
- Check how it looks in Gmail on your phone and Gmail on your desktop
- Check the Resend dashboard → Emails to confirm delivery status
For systematic cross-client testing, Email on Acid and Litmus are professional tools — both have free trials. For training purposes, testing in Gmail is sufficient.
Next Step
You have the templates written and the API key in place. The final page is a checklist to confirm everything is working end-to-end before you consider Resend fully set up.