Skip to main content
Back to Blog
Tutorials
4 min read
November 27, 2024

How to Build a Responsive Email Template with React Email

Create beautiful, responsive email templates using React Email with components for transactional and marketing emails.

Ryel Banfield

Founder & Lead Developer

React Email lets you build email templates with React components. Here is how to create reusable, responsive emails.

Step 1: Install React Email

pnpm add @react-email/components react-email

Step 2: Email Layout Component

// emails/components/Layout.tsx
import {
  Html,
  Head,
  Body,
  Container,
  Section,
  Img,
  Text,
  Link,
  Hr,
  Preview,
} from "@react-email/components";

interface LayoutProps {
  preview: string;
  children: React.ReactNode;
}

export function EmailLayout({ preview, children }: LayoutProps) {
  return (
    <Html>
      <Head />
      <Preview>{preview}</Preview>
      <Body style={body}>
        <Container style={container}>
          {/* Header */}
          <Section style={header}>
            <Img
              src="https://rcbsoftware.com/logos/logo.png"
              width={120}
              height={32}
              alt="RCB Software"
            />
          </Section>

          {/* Content */}
          <Section style={content}>{children}</Section>

          {/* Footer */}
          <Hr style={hr} />
          <Section style={footer}>
            <Text style={footerText}>
              RCB Software — Custom web and software solutions
            </Text>
            <Text style={footerLinks}>
              <Link href="https://rcbsoftware.com" style={link}>
                Website
              </Link>
              {" · "}
              <Link href="https://rcbsoftware.com/contact" style={link}>
                Contact
              </Link>
              {" · "}
              <Link href="%unsubscribe_url%" style={link}>
                Unsubscribe
              </Link>
            </Text>
          </Section>
        </Container>
      </Body>
    </Html>
  );
}

const body = {
  backgroundColor: "#f6f9fc",
  fontFamily:
    '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
};

const container = {
  backgroundColor: "#ffffff",
  margin: "0 auto",
  padding: "0",
  maxWidth: "600px",
  borderRadius: "8px",
  overflow: "hidden" as const,
};

const header = {
  backgroundColor: "#0f172a",
  padding: "24px 32px",
};

const content = {
  padding: "32px",
};

const hr = {
  borderColor: "#e5e7eb",
  margin: "0",
};

const footer = {
  padding: "24px 32px",
};

const footerText = {
  color: "#6b7280",
  fontSize: "12px",
  lineHeight: "20px",
};

const footerLinks = {
  color: "#6b7280",
  fontSize: "12px",
};

const link = {
  color: "#3b82f6",
  textDecoration: "none" as const,
};

Step 3: Welcome Email

// emails/WelcomeEmail.tsx
import { Text, Button, Section, Heading } from "@react-email/components";
import { EmailLayout } from "./components/Layout";

interface WelcomeEmailProps {
  name: string;
  loginUrl: string;
}

export default function WelcomeEmail({ name, loginUrl }: WelcomeEmailProps) {
  return (
    <EmailLayout preview={`Welcome to RCB Software, ${name}!`}>
      <Heading as="h1" style={heading}>
        Welcome aboard, {name}!
      </Heading>

      <Text style={text}>
        We are excited to have you. Your account is ready, and you can start
        exploring everything we have to offer right away.
      </Text>

      <Section style={{ textAlign: "center" as const, marginTop: "32px" }}>
        <Button style={button} href={loginUrl}>
          Get Started
        </Button>
      </Section>

      <Text style={text}>
        Here is what you can do next:
      </Text>

      <ul>
        <li style={listItem}>Complete your profile setup</li>
        <li style={listItem}>Explore the dashboard</li>
        <li style={listItem}>Create your first project</li>
      </ul>

      <Text style={text}>
        If you have any questions, just reply to this email — we are always happy to help.
      </Text>

      <Text style={signoff}>
        Best,
        <br />
        The RCB Software Team
      </Text>
    </EmailLayout>
  );
}

const heading = {
  fontSize: "24px",
  fontWeight: "bold" as const,
  color: "#0f172a",
  lineHeight: "32px",
};

const text = {
  fontSize: "14px",
  lineHeight: "24px",
  color: "#374151",
};

const button = {
  backgroundColor: "#3b82f6",
  borderRadius: "6px",
  color: "#ffffff",
  fontSize: "14px",
  fontWeight: "600" as const,
  padding: "12px 24px",
  textDecoration: "none" as const,
};

const listItem = {
  fontSize: "14px",
  lineHeight: "28px",
  color: "#374151",
};

const signoff = {
  fontSize: "14px",
  color: "#374151",
  marginTop: "32px",
};

Step 4: Invoice Email

// emails/InvoiceEmail.tsx
import { Text, Section, Row, Column, Heading, Hr, Button } from "@react-email/components";
import { EmailLayout } from "./components/Layout";

interface LineItem {
  description: string;
  quantity: number;
  unitPrice: number;
}

interface InvoiceEmailProps {
  customerName: string;
  invoiceNumber: string;
  dueDate: string;
  items: LineItem[];
  invoiceUrl: string;
}

export default function InvoiceEmail({
  customerName,
  invoiceNumber,
  dueDate,
  items,
  invoiceUrl,
}: InvoiceEmailProps) {
  const subtotal = items.reduce((sum, item) => sum + item.quantity * item.unitPrice, 0);
  const tax = subtotal * 0.1;
  const total = subtotal + tax;

  return (
    <EmailLayout preview={`Invoice ${invoiceNumber} — $${total.toFixed(2)} due ${dueDate}`}>
      <Heading as="h1" style={heading}>
        Invoice #{invoiceNumber}
      </Heading>

      <Text style={text}>Hi {customerName},</Text>
      <Text style={text}>
        Here is your invoice. Payment is due by {dueDate}.
      </Text>

      {/* Line items */}
      <Section style={table}>
        <Row style={tableHeader}>
          <Column style={{ ...cell, width: "50%" }}>Description</Column>
          <Column style={{ ...cell, width: "15%", textAlign: "right" as const }}>Qty</Column>
          <Column style={{ ...cell, width: "17%", textAlign: "right" as const }}>Price</Column>
          <Column style={{ ...cell, width: "18%", textAlign: "right" as const }}>Total</Column>
        </Row>

        {items.map((item, i) => (
          <Row key={i} style={tableRow}>
            <Column style={cell}>{item.description}</Column>
            <Column style={{ ...cell, textAlign: "right" as const }}>{item.quantity}</Column>
            <Column style={{ ...cell, textAlign: "right" as const }}>
              ${item.unitPrice.toFixed(2)}
            </Column>
            <Column style={{ ...cell, textAlign: "right" as const }}>
              ${(item.quantity * item.unitPrice).toFixed(2)}
            </Column>
          </Row>
        ))}
      </Section>

      <Hr style={hr} />

      {/* Totals */}
      <Section>
        <Row>
          <Column style={{ width: "65%" }} />
          <Column style={totalLabel}>Subtotal</Column>
          <Column style={totalValue}>${subtotal.toFixed(2)}</Column>
        </Row>
        <Row>
          <Column style={{ width: "65%" }} />
          <Column style={totalLabel}>Tax (10%)</Column>
          <Column style={totalValue}>${tax.toFixed(2)}</Column>
        </Row>
        <Row>
          <Column style={{ width: "65%" }} />
          <Column style={{ ...totalLabel, fontWeight: "bold" as const }}>Total</Column>
          <Column style={{ ...totalValue, fontWeight: "bold" as const, fontSize: "16px" }}>
            ${total.toFixed(2)}
          </Column>
        </Row>
      </Section>

      <Section style={{ textAlign: "center" as const, marginTop: "32px" }}>
        <Button style={button} href={invoiceUrl}>
          Pay Invoice
        </Button>
      </Section>
    </EmailLayout>
  );
}

const heading = { fontSize: "24px", fontWeight: "bold" as const, color: "#0f172a" };
const text = { fontSize: "14px", lineHeight: "24px", color: "#374151" };
const table = { marginTop: "24px" };
const tableHeader = { backgroundColor: "#f9fafb", borderBottom: "1px solid #e5e7eb" };
const tableRow = { borderBottom: "1px solid #f3f4f6" };
const cell = { padding: "10px 8px", fontSize: "13px", color: "#374151" };
const hr = { borderColor: "#e5e7eb", margin: "16px 0" };
const totalLabel = { textAlign: "right" as const, fontSize: "13px", color: "#6b7280", padding: "4px 8px" };
const totalValue = { textAlign: "right" as const, fontSize: "13px", color: "#0f172a", padding: "4px 8px" };
const button = {
  backgroundColor: "#3b82f6",
  borderRadius: "6px",
  color: "#ffffff",
  fontSize: "14px",
  fontWeight: "600" as const,
  padding: "12px 24px",
  textDecoration: "none" as const,
};

Step 5: Send with Resend

// lib/email.ts
import { Resend } from "resend";
import { render } from "@react-email/render";
import WelcomeEmail from "@/emails/WelcomeEmail";
import InvoiceEmail from "@/emails/InvoiceEmail";

const resend = new Resend(process.env.RESEND_API_KEY);

export async function sendWelcomeEmail(to: string, name: string) {
  const html = await render(
    WelcomeEmail({
      name,
      loginUrl: `${process.env.NEXT_PUBLIC_APP_URL}/login`,
    })
  );

  return resend.emails.send({
    from: "RCB Software <hello@rcbsoftware.com>",
    to,
    subject: `Welcome to RCB Software, ${name}!`,
    html,
  });
}

export async function sendInvoiceEmail(
  to: string,
  data: {
    customerName: string;
    invoiceNumber: string;
    dueDate: string;
    items: { description: string; quantity: number; unitPrice: number }[];
    invoiceUrl: string;
  }
) {
  const html = await render(InvoiceEmail(data));

  return resend.emails.send({
    from: "RCB Software <billing@rcbsoftware.com>",
    to,
    subject: `Invoice #${data.invoiceNumber}`,
    html,
  });
}

Step 6: Preview Emails in Development

Add the dev script to package.json:

{
  "scripts": {
    "email": "email dev --dir emails --port 3001"
  }
}

Run pnpm email to preview and test your emails at localhost:3001.

Summary

  • React Email components render to HTML email-compatible output
  • Shared layout ensures consistent branding across all emails
  • Inline styles work reliably across email clients
  • React Email dev server provides instant preview during development

Need Professional Email Templates?

We design and develop email systems that engage users and drive conversions. Contact us to get started.

emailReact Emailtemplatesresponsivetutorial

Ready to Start Your Project?

RCB Software builds world-class websites and applications for businesses worldwide.

Get in Touch

Related Articles