Overview
The Contiguity SDK integrates with React Email to let you build emails using React components. The SDK automatically renders your components to HTML and plain text.
Installation
Install React Email and its dependencies:
npm install @react-email/render react react-dom
React Email requires react and react-dom as peer dependencies.
Quick Start
Using the react Parameter
Pass React components directly to the react parameter:
import { Contiguity } from 'contiguity';
import { Html, Text, Button } from '@react-email/components';
import * as React from 'react';
const client = new Contiguity();
await client.email.send({
to: '[email protected]',
from: '[email protected]',
subject: 'Welcome to our platform!',
react: (
<Html>
<Text>Welcome aboard!</Text>
<Button href="https://example.com/get-started">
Get Started
</Button>
</Html>
)
});
The SDK automatically:
- Renders the component to HTML
- Generates plain text version
- Sends both versions in the email
Using renderReactEmail Helper
For more control, use the renderReactEmail function:
import { Contiguity, renderReactEmail } from 'contiguity';
import { WelcomeEmail } from './emails/welcome';
const client = new Contiguity();
// Render component
const { html, text } = await renderReactEmail(
<WelcomeEmail name="Alice" />
);
// Send with rendered content
await client.email.send({
to: '[email protected]',
from: '[email protected]',
subject: 'Welcome!',
html,
text
});
Creating Email Templates
Simple Template
import { Html, Head, Body, Container, Text, Button } from '@react-email/components';
import * as React from 'react';
interface WelcomeEmailProps {
name: string;
}
export function WelcomeEmail({ name }: WelcomeEmailProps) {
return (
<Html>
<Head />
<Body style={{ backgroundColor: '#f6f9fc' }}>
<Container>
<Text>Hi {name},</Text>
<Text>Welcome to our platform!</Text>
<Button
href="https://example.com/get-started"
style={{ background: '#5469d4', color: '#fff' }}
>
Get Started
</Button>
</Container>
</Body>
</Html>
);
}
Template with Styling
import {
Html,
Head,
Body,
Container,
Section,
Text,
Button,
Hr
} from '@react-email/components';
import * as React from 'react';
interface NotificationEmailProps {
title: string;
message: string;
actionUrl?: string;
actionText?: string;
}
export function NotificationEmail({
title,
message,
actionUrl,
actionText
}: NotificationEmailProps) {
return (
<Html>
<Head />
<Body style={main}>
<Container style={container}>
<Section>
<Text style={heading}>{title}</Text>
<Text style={paragraph}>{message}</Text>
{actionUrl && actionText && (
<>
<Button style={button} href={actionUrl}>
{actionText}
</Button>
</>
)}
<Hr style={hr} />
<Text style={footer}>
© 2024 Your Company. All rights reserved.
</Text>
</Section>
</Container>
</Body>
</Html>
);
}
const main = {
backgroundColor: '#f6f9fc',
fontFamily: '-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif'
};
const container = {
margin: '0 auto',
padding: '20px 0 48px',
maxWidth: '560px'
};
const heading = {
fontSize: '24px',
fontWeight: 'bold',
marginBottom: '16px'
};
const paragraph = {
fontSize: '16px',
lineHeight: '26px',
color: '#525f7f'
};
const button = {
backgroundColor: '#5469d4',
borderRadius: '5px',
color: '#fff',
fontSize: '16px',
textDecoration: 'none',
textAlign: 'center' as const,
display: 'block',
padding: '12px'
};
const hr = {
borderColor: '#e6ebf1',
margin: '20px 0'
};
const footer = {
color: '#8898aa',
fontSize: '12px',
lineHeight: '16px'
};
Sending Templates
With the react Parameter
import { WelcomeEmail } from './emails/welcome';
await client.email.send({
to: '[email protected]',
from: '[email protected]',
subject: 'Welcome!',
react: <WelcomeEmail name="Alice" />
});
Pre-rendering Templates
import { renderReactEmail } from 'contiguity';
import { NotificationEmail } from './emails/notification';
// Render once
const { html, text } = await renderReactEmail(
<NotificationEmail
title="New Message"
message="You have a new message from support."
actionUrl="https://example.com/messages"
actionText="View Message"
/>
);
// Send to multiple recipients
for (const recipient of recipients) {
await client.email.send({
to: recipient,
from: '[email protected]',
subject: 'New Message',
html,
text
});
}
How It Works
The renderReactEmail function:
- Imports React Email dynamically
- Renders component to HTML using
@react-email/render
- Generates plain text version automatically
- Returns both formats for email sending
export async function renderReactEmail(
node: unknown
): Promise<{ html: string; text: string }> {
const mod = await import("@react-email/render").catch(() => {
throw new Error(
"Failed to render React component. Install `@react-email/render` (and peer deps react, react-dom)."
);
});
const html = await mod.render(node);
const text = mod.toPlainText(html);
return { html, text };
}
The SDK handles React Email imports dynamically, so you only need to install it if you use React components for emails.
Available React Email Components
React Email provides many built-in components:
Html - Root HTML wrapper
Head - Document head
Body - Document body
Container - Centered container
Section - Content section
Row / Column - Grid layout
Text - Paragraph text
Heading - Headings (h1-h6)
Button - Call-to-action button
Link - Hyperlink
Img - Image
Hr - Horizontal rule
Code - Code block
Preview - Email preview text
See the React Email documentation for all components.
Email Template Examples
Password Reset Email
import { Html, Body, Container, Text, Button, Section } from '@react-email/components';
interface PasswordResetEmailProps {
resetUrl: string;
expirationMinutes: number;
}
export function PasswordResetEmail({ resetUrl, expirationMinutes }: PasswordResetEmailProps) {
return (
<Html>
<Body>
<Container>
<Section>
<Text>Password Reset Request</Text>
<Text>
Click the button below to reset your password. This link will expire in {expirationMinutes} minutes.
</Text>
<Button href={resetUrl}>Reset Password</Button>
<Text>
If you didn't request this, you can safely ignore this email.
</Text>
</Section>
</Container>
</Body>
</Html>
);
}
// Send it
await client.email.send({
to: '[email protected]',
from: '[email protected]',
subject: 'Reset your password',
react: <PasswordResetEmail resetUrl="https://example.com/reset?token=..." expirationMinutes={30} />
});
Order Confirmation Email
import { Html, Body, Container, Text, Section, Hr } from '@react-email/components';
interface OrderItem {
name: string;
quantity: number;
price: number;
}
interface OrderConfirmationEmailProps {
orderNumber: string;
items: OrderItem[];
total: number;
}
export function OrderConfirmationEmail({ orderNumber, items, total }: OrderConfirmationEmailProps) {
return (
<Html>
<Body>
<Container>
<Text>Order Confirmation</Text>
<Text>Order #{orderNumber}</Text>
<Hr />
{items.map((item, i) => (
<Section key={i}>
<Text>
{item.quantity}x {item.name} - ${item.price.toFixed(2)}
</Text>
</Section>
))}
<Hr />
<Text>Total: ${total.toFixed(2)}</Text>
<Text>Thank you for your order!</Text>
</Container>
</Body>
</Html>
);
}
// Send it
await client.email.send({
to: '[email protected]',
from: '[email protected]',
subject: `Order Confirmation #${orderNumber}`,
react: (
<OrderConfirmationEmail
orderNumber="12345"
items={[
{ name: 'Widget', quantity: 2, price: 29.99 },
{ name: 'Gadget', quantity: 1, price: 49.99 }
]}
total={109.97}
/>
)
});
Best Practices
1. Always Provide Plain Text
React Email automatically generates plain text, but you can customize it:
const { html, text } = await renderReactEmail(<WelcomeEmail name="Alice" />);
// Customize plain text if needed
const customText = text.replace(/\n{3,}/g, '\n\n'); // Remove extra newlines
await client.email.send({
to: '[email protected]',
from: '[email protected]',
subject: 'Welcome!',
html,
text: customText
});
2. Use Inline Styles
Email clients don’t support external stylesheets. Use inline styles:
<Text style={{ fontSize: '16px', color: '#333' }}>
Your content here
</Text>
3. Test Email Rendering
Develop and preview emails locally using React Email CLI:
4. Keep Templates Reusable
Extract common layouts into shared components:
interface EmailLayoutProps {
children: React.ReactNode;
}
export function EmailLayout({ children }: EmailLayoutProps) {
return (
<Html>
<Head />
<Body style={{ backgroundColor: '#f6f9fc' }}>
<Container style={{ maxWidth: '560px', margin: '0 auto' }}>
{children}
<Hr />
<Text style={{ fontSize: '12px', color: '#8898aa' }}>
© 2024 Your Company. All rights reserved.
</Text>
</Container>
</Body>
</Html>
);
}
Error Handling
Handle missing dependencies gracefully:
import { ContiguityError, renderReactEmail } from 'contiguity';
try {
const { html, text } = await renderReactEmail(<WelcomeEmail name="Alice" />);
await client.email.send({ to: '[email protected]', from: '[email protected]', subject: 'Welcome', html, text });
} catch (error) {
if (error instanceof Error && error.message.includes('@react-email/render')) {
console.error('React Email not installed. Run: npm install @react-email/render react react-dom');
} else if (error instanceof ContiguityError) {
console.error('API error:', error.message);
} else {
throw error;
}
}
For production use, pre-render templates at build time and cache the HTML/text to avoid runtime overhead.