XYLEX Group

email templates

email templates

this guide covers creating customizing and using email templates with react email

table of contents

  • available templates
  • template variables
  • creating new templates
  • template structure
  • styling templates
  • previewing templates
  • best practices

available templates

| template id | purpose | category | |-------------|---------|----------| | invoice | invoice notification to customers | transactional | | company-invite | team member invitation | notification | | quote-recipient | quote sent to customer | transactional | | quote-author | quote viewed notification | notification | | batch-invoice | multiple invoices notification | transactional | | password-reset | password reset request | transactional | | welcome | new user welcome | notification | | verification | email verification | transactional | | payment-received | payment confirmation | transactional | | payment-reminder | payment reminder | transactional | | custom | custom html (no template) | - |


template variables

templates use {{VARIABLE_NAME}} syntax for dynamic content variables are replaced at send time

invoice template variables

{
  AUTHOR_NAME: "Your Company Name",
  RECIPIENT_NAME: "Customer Name",
  AMOUNT_TOTAL: "€1,234.56",
  DUE_DATE: "December 15, 2025",
  INVOICE_NUMBER: "INV-2025-001",
  INVOICE_URL: "https://app.suitsbooks.com/invoices/...",
  LINE1_PRODUCT_NAME: "Product Name",
  LINE1_QTY: "2",
  LINE1_UNIT_PRICE: "€100.00",
  LINE1_SUBTOTAL: "€200.00",
  amount_paid: "€0.00",
  amount_remaining: "€1,234.56",
  FOOTER_TEXT: "Thank you for your business"
}
```typescript

### company invite variables

```typescript
{
  TEAM_NAME: "Finance Team",
  ORGANIZATION_NAME: "SuitsBooks",
  RECIPIENT_NAME: "John Doe",
  SENDER_NAME: "Jane Smith",
  SENDER_EMAIL: "jane@company.com",
  INVITE_LINK: "https://app.suitsbooks.com/invite/...",
  AUTHOR_COMPANY_LOGO_URL: "https://...",
  RECIPIENT_AVATAR: "https://...",
  SENDER_IP: "192.168.1.1",
  SENDER_LOCATION: "Amsterdam, Netherlands"
}
```typescript

### welcome template variables

```typescript
{
  USER_NAME: "John Doe",
  USER_EMAIL: "john@example.com",
  LOGIN_URL: "https://app.suitsbooks.com/login",
  SUPPORT_EMAIL: "support@suitsbooks.com"
}
```typescript

### password reset variables

```typescript
{
  USER_NAME: "John Doe",
  RESET_URL: "https://app.suitsbooks.com/reset-password?token=...",
  EXPIRY_TIME: "24 hours",
  SUPPORT_EMAIL: "support@suitsbooks.com",
  REQUEST_IP: "192.168.1.1",
  REQUEST_LOCATION: "Amsterdam, Netherlands"
}
```typescript

### verification template variables

```typescript
{
  USER_NAME: "John Doe",
  VERIFICATION_URL: "https://app.suitsbooks.com/verify?token=...",
  VERIFICATION_CODE: "123456",
  EXPIRY_TIME: "24 hours",
  SUPPORT_EMAIL: "support@suitsbooks.com"
}
```typescript

### payment reminder variables

```typescript
{
  RECIPIENT_NAME: "Customer Name",
  AUTHOR_NAME: "Your Company",
  INVOICE_NUMBER: "INV-2025-001",
  AMOUNT_DUE: "€1,234.56",
  DUE_DATE: "December 1, 2025",
  DAYS_OVERDUE: "14",
  INVOICE_URL: "https://app.suitsbooks.com/invoices/...",
  SUPPORT_EMAIL: "support@suitsbooks.com"
}
```typescript

### payment received variables

```typescript
{
  RECIPIENT_NAME: "Customer Name",
  AUTHOR_NAME: "Your Company",
  INVOICE_NUMBER: "INV-2025-001",
  AMOUNT_PAID: "€1,234.56",
  PAYMENT_DATE: "December 1, 2025",
  PAYMENT_METHOD: "Bank Transfer",
  RECEIPT_URL: "https://app.suitsbooks.com/receipts/...",
  SUPPORT_EMAIL: "support@suitsbooks.com"
}
```typescript

---

## creating new templates

### step 1: create the template file

create a new file in emails/templates/

```tsx
// emails/templates/order-confirmation.tsx
import {
  Body,
  Button,
  Container,
  Head,
  Heading,
  Html,
  Preview,
  Section,
  Tailwind,
  Text,
} from "@react-email/components";
import React from "react";

/**
 * order confirmation email template
 *
 * variables:
 * - {{CUSTOMER_NAME}} - customers name
 * - {{ORDER_NUMBER}} - order number
 * - {{ORDER_TOTAL}} - total amount
 * - {{ORDER_URL}} - url to view order
 */
export default function OrderConfirmationEmail() {
  return (
    <Html>
      <Head>
        <meta name="color-scheme" content="light" />
        <meta name="supported-color-schemes" content="light" />
      </Head>
      <Preview>Your order {"{{ORDER_NUMBER}}"} has been confirmed</Preview>
      <Tailwind
        config={{
          theme: {
            extend: {
              colors: {
                brand: "#4085e6",
              },
            },
          },
        }}
      >
        <Body className="m-0 w-full bg-[#fafafa] font-sans">
          <Container className="mx-auto max-w-[600px] px-4 py-8">
            <Section
              className="rounded-lg bg-white p-8 shadow-sm"
              style={{ border: "1px solid #e5e5e5" }}
            >
              <Heading className="m-0 mb-4 text-2xl font-semibold text-gray-800">
                order confirmed
              </Heading>

              <Text className="mb-4 text-base text-gray-600">
                hi {"{{CUSTOMER_NAME}}"},
              </Text>

              <Text className="mb-4 text-base text-gray-600">
                thank you for your order your order number is{" "}
                <strong>{"{{ORDER_NUMBER}}"}</strong>.
              </Text>

              <Text className="mb-4 text-base text-gray-600">
                order total: <strong>{"{{ORDER_TOTAL}}"}</strong>
              </Text>

              <Section className="my-6 text-center">
                <Button
                  href={"{{ORDER_URL}}"}
                  className="rounded-sm bg-brand px-6 py-3 text-base font-semibold text-white no-underline"
                >
                  view order
                </Button>
              </Section>
            </Section>
          </Container>
        </Body>
      </Tailwind>
    </Html>
  );
}
```typescript

### step 2: register the template

add the template to emails/templates/indexts

```tsx
import OrderConfirmationEmail from "./order-confirmation";

export const EMAIL_TEMPLATES: Record<EmailTemplateId, React.ComponentType> = {
  // ... existing templates
  "order-confirmation": OrderConfirmationEmail,
};
```typescript

### step 3: add type definition

update lib/email/typests

```typescript
export type EmailTemplateId =
  | "invoice"
  | "company-invite"
  // ... existing types
  | "order-confirmation";  // add new template
```typescript

### step 4: add configuration

update lib/email/configts

```typescript
export const TEMPLATE_CONFIG: Record<EmailTemplateId, {...}> = {
  // ... existing configs
  "order-confirmation": {
    defaultSubject: "Order {{ORDER_NUMBER}} confirmed",
    requiresAuth: true,
    category: "transactional",
  },
};
```typescript

### step 5: use the template

```tsx
await sendEmail({
  to: "customer@example.com",
  subject: "Order #12345 confirmed",
  template_id: "order-confirmation",
  template_data: {
    CUSTOMER_NAME: "John Doe",
    ORDER_NUMBER: "12345",
    ORDER_TOTAL: "€99.99",
    ORDER_URL: "https://app.suitsbooks.com/orders/12345",
  },
});
```typescript

---

## template structure

### recommended structure

```tsx
import {
  Body,
  Button,
  Container,
  Head,
  Heading,
  Hr,
  Html,
  Link,
  Preview,
  Section,
  Tailwind,
  Text,
} from "@react-email/components";
import React from "react";

export default function MyTemplate() {
  return (
    <Html>
      {/* 1. head - meta tags */}
      <Head>
        <meta name="color-scheme" content="light" />
        <meta name="supported-color-schemes" content="light" />
      </Head>

      {/* 2. preview - shows in email client preview */}
      <Preview>Preview text shown in inbox</Preview>

      {/* 3. tailwind - styling configuration */}
      <Tailwind
        config={{
          theme: {
            extend: {
              colors: {
                brand: "#4085e6",
              },
            },
          },
        }}
      >
        {/* 4. body - main wrapper */}
        <Body className="m-0 w-full bg-[#fafafa] font-sans">
          {/* 5. container - content width */}
          <Container className="mx-auto max-w-[600px] px-4 py-8">
            
            {/* 6. header section */}
            <Section className="mb-6 text-center">
              <Text className="text-2xl font-bold text-brand">SuitsBooks</Text>
            </Section>

            {/* 7. main content card */}
            <Section
              className="rounded-lg bg-white p-8 shadow-sm"
              style={{ border: "1px solid #e5e5e5" }}
            >
              <Heading>title</Heading>
              <Text>content...</Text>
              <Button href="{{CTA_URL}}">call to action</Button>
            </Section>

            {/* 8. footer */}
            <Section className="mt-8 text-center">
              <Text className="text-xs text-gray-400">
                © {new Date().getFullYear()} SuitsBooks
              </Text>
            </Section>

          </Container>
        </Body>
      </Tailwind>
    </Html>
  );
}
```typescript

### using the base layout

for consistency you can use the base layout

```tsx
import { BaseLayout } from "./base-layout";

export default function MyTemplate() {
  return (
    <BaseLayout preview="email preview text" footerText="custom footer">
      {/* your content here */}
    </BaseLayout>
  );
}
```typescript

---

## styling templates

### tailwind css

templates use tailwind css via the Tailwind component

```tsx
<Tailwind
  config={{
    theme: {
      extend: {
        colors: {
          brand: "#4085e6",
          "brand-dark": "#2563eb",
        },
      },
    },
  }}
>
  <Body className="bg-gray-100">
    <Text className="text-brand font-bold">styled text</Text>
  </Body>
</Tailwind>
```typescript

### inline styles

for complex styles or email client compatibility

```tsx
<Section
  className="rounded-lg bg-white p-8"
  style={{
    border: "1px solid #e5e5e5",
    boxShadow: "0 1px 3px rgba(0,0,0,0.1)",
  }}
>
```typescript

### email-safe styles

not all css properties work in email clients safe properties include

- background-color color
- font-family font-size font-weight
- padding margin
- border border-radius
- width max-width
- text-align

avoid

- flexbox (limited support)
- grid (poor support)
- position (poor support)
- box-shadow (limited support)

---

## previewing templates

### using react email dev server

```bash
pnpm email
```typescript

this starts the react email dev server at http://localhost:3040 where you can

- preview all templates
- test with different variables
- view html source
- send test emails

### manual preview

```tsx
import { render } from "@react-email/components";
import WelcomeEmail from "@/emails/templates/welcome";

// render to html string
const html = await render(WelcomeEmail());
console.log(html);
```typescript

---

## best practices

### 1 use semantic variable names

```typescript
// good
{
  CUSTOMER_NAME: "John",
  ORDER_TOTAL: "€99.99",
  DELIVERY_DATE: "Dec 15, 2025"
}

// bad
{
  name: "John",
  total: "€99.99",
  date: "Dec 15, 2025"
}
```typescript

### 2 provide fallback content

```tsx
<Text>
  hi {"{{CUSTOMER_NAME}}"},  {/* if not provided shows literally */}
</Text>

// better approach in your sending code
template_data: {
  CUSTOMER_NAME: customer.name || "there",
}
```typescript

### 3 keep templates simple

- avoid complex layouts
- use tables for alignment (better email client support)
- test in multiple email clients

### 4 include preview text

```tsx
<Preview>
  your invoice for €1,234.56 is ready to view
</Preview>
```typescript

### 5 add alt text for images

```tsx
<img
  src="{{LOGO_URL}}"
  alt="Company Logo"
  width={120}
  height={40}
/>
```typescript

### 6 use absolute urls

```tsx
// good
<Button href="https://app.suitsbooks.com/invoice/123">
  view invoice
</Button>

// bad
<Button href="/invoice/123">
  view invoice
</Button>
```typescript

### 7 document your variables

```tsx
/**
 * invoice email template
 *
 * required variables:
 * - {{RECIPIENT_NAME}} - customers full name
 * - {{INVOICE_NUMBER}} - invoice identifier
 * - {{AMOUNT_TOTAL}} - formatted total amount
 * - {{DUE_DATE}} - formatted due date
 * - {{INVOICE_URL}} - full url to view invoice
 *
 * optional variables:
 * - {{AUTHOR_NAME}} - sender company name (default: from config)
 */
export default function InvoiceEmail() {
```typescript

### 8 test email client compatibility

test your templates in

- gmail (web ios android)
- outlook (web desktop ios)
- apple mail
- yahoo mail
- mobile clients

use tools like litmus or email on acid for comprehensive testing

---

## troubleshooting

### variables not replacing

ensure you are using the correct syntax

```tsx
// correct
{"{{VARIABLE_NAME}}"}

// wrong (missing braces in jsx)
{`{{VARIABLE_NAME}}`}

styles not applying

1 check email client support 2 use inline styles for critical styles 3 avoid shorthand properties

template not found

1 ensure template is exported from emails/templates/indexts 2 check template id matches exactly 3 verify type is added to EmailTemplateId

preview not working

# clear cache and restart
rm -rf node_modules/.cache
pnpm email
```typescript