React Email Components: Build HTML Emails with React
Welcome to TopperBlog! 👋
I'm a tech content creator passionate about helping developers level up their careers and master cutting-edge technologies.
🎯 What I Write About:
• AI/ML Engineering & LLMs
• Web3 & Blockchain Development
• System Design & Architecture
• Interview Preparation (FAANG)
• Freelancing & Remote Work
• Modern Tech Stacks (Next.js, React, Rust, TypeScript)
• Performance Optimization & Best Practices
💼 Mission: Sharing practical, actionable insights that accelerate your tech career and maximize your earning potential.
📚 15+ In-Depth Guides covering everything from earning $10k/month as a freelancer to cracking FAANG interviews.
🌐 Let's connect and grow together in this amazing tech journey!
#TechBlogger #SoftwareEngineering #CareerGrowth #WebDevelopment #AIEngineering
React Email Components: Build HTML Emails with React
The Search That Cost Us 10,000 Users (And How We Fixed It)
Our search was broken. Users couldn't find anything. Here's how we rebuilt it in 48 hours.
Table of Contents
- Search & Email 2026
- Architecture Fundamentals
- 5 Implementation Patterns
- Performance Optimization
- Relevance Tuning
- Analytics Integration
- Scaling Strategy
- FAQ
- Production Checklist
Search & Email in 2026
Discovery and communication are critical.
User Expectations
// Modern search requirements
interface SearchRequirements {
speed: '<50ms response time';
typoTolerance: 'Fix misspellings automatically';
relevance: 'Show best results first';
filtering: 'Multi-faceted filtering';
highlighting: 'Show matching text';
}
Email Deliverability
// Email success metrics
const emailMetrics = {
deliveryRate: 0.98, // 98% delivered
openRate: 0.25, // 25% opened
clickRate: 0.05, // 5% clicked
bounceRate: 0.02, // 2% bounced
spamRate: 0.001 // 0.1% marked spam
};
Business Impact
Good search = happy users = more conversions.
Architecture Fundamentals
Building scalable search and email.
Search Index
// Index documents
interface Document {
id: string;
title: string;
content: string;
category: string;
tags: string[];
createdAt: number;
metadata: Record<string, any>;
}
// Create index
const index = await searchClient.createIndex('products', {
primaryKey: 'id',
searchableAttributes: ['title', 'content'],
filterableAttributes: ['category', 'tags'],
sortableAttributes: ['createdAt', 'price']
});
// Add documents
await index.addDocuments([
{
id: '1',
title: 'Product Name',
content: 'Description...',
category: 'electronics',
tags: ['new', 'sale']
}
]);
Email Templates
// React Email component
import { Html, Head, Body, Container, Text } from '@react-email/components';
export function WelcomeEmail({ name }: { name: string }) {
return (
<Html>
<Head />
<Body style={{ backgroundColor: '#f6f9fc' }}>
<Container>
<Text style={{ fontSize: 24, fontWeight: 'bold' }}>
Welcome, {name}!
</Text>
<Text>
Thanks for signing up. Get started with these resources.
</Text>
</Container>
</Body>
</Html>
);
}
Pattern 1: Instant Search
Frontend Implementation
// React search component
import { useSearch } from './hooks';
function SearchBox() {
const { query, setQuery, results, isLoading } = useSearch();
return (
<div>
<input
type="text"
value={query}
onChange={e => setQuery(e.target.value)}
placeholder="Search..."
/>
{isLoading && <Spinner />}
<div>
{results.map(result => (
<SearchResult key={result.id} data={result} />
))}
</div>
</div>
);
}
// Custom hook
function useSearch() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
if (!query) {
setResults([]);
return;
}
setIsLoading(true);
const timeoutId = setTimeout(async () => {
const data = await searchAPI.search(query);
setResults(data.hits);
setIsLoading(false);
}, 300); // Debounce
return () => clearTimeout(timeoutId);
}, [query]);
return { query, setQuery, results, isLoading };
}
Highlighting
// Highlight matching terms
function highlightMatches(text: string, query: string): string {
const regex = new RegExp(`(${query})`, 'gi');
return text.replace(regex, '<mark>$1</mark>');
}
Pattern 2: Faceted Search
Filters
// Multi-faceted filtering
interface SearchFilters {
category?: string[];
tags?: string[];
priceRange?: { min: number; max: number };
dateRange?: { start: Date; end: Date };
}
async function search(
query: string,
filters: SearchFilters
) {
const results = await searchClient.search(query, {
filter: [
filters.category?.map(c => `category = ${c}`).join(' OR '),
filters.tags?.map(t => `tags = ${t}`).join(' OR '),
filters.priceRange &&
`price >= ${filters.priceRange.min} AND price <= ${filters.priceRange.max}`
].filter(Boolean).join(' AND ')
});
return results;
}
Aggregations
// Get filter counts
const facets = await searchClient.getFacets('products', {
facets: ['category', 'tags'],
query: 'laptop'
});
// Result:
// {
// category: { 'electronics': 123, 'computers': 89 },
// tags: { 'sale': 45, 'new': 67 }
// }
Pattern 3: Email Sending
Transactional Emails
// Send transactional email
import { Resend } from 'resend';
const resend = new Resend(process.env.RESEND_API_KEY);
async function sendWelcomeEmail(user: User) {
const { data, error } = await resend.emails.send({
from: 'noreply@example.com',
to: user.email,
subject: 'Welcome to Our Platform!',
react: WelcomeEmail({ name: user.name })
});
if (error) {
logger.error('Email send failed', { error, userId: user.id });
throw error;
}
// Track sent email
await db.emails.create({
userId: user.id,
type: 'welcome',
provider: 'resend',
messageId: data.id,
sentAt: new Date()
});
}
Batch Sending
// Send to multiple recipients
async function sendBulkEmails(users: User[], template: string) {
const BATCH_SIZE = 100;
for (let i = 0; i < users.length; i += BATCH_SIZE) {
const batch = users.slice(i, i + BATCH_SIZE);
await Promise.all(
batch.map(user =>
resend.emails.send({
from: 'newsletter@example.com',
to: user.email,
subject: getSubject(template, user),
react: getTemplate(template, user)
})
)
);
// Rate limit between batches
await delay(1000);
}
}
Pattern 4: Personalization
Search Ranking
// Personalized results
async function personalizedSearch(
query: string,
userId: string
) {
// Get user preferences
const prefs = await getUserPreferences(userId);
// Boost relevant categories
const results = await searchClient.search(query, {
rankingRules: [
'typo',
'words',
'proximity',
'attribute',
'sort',
'exactness',
// Custom: boost user's interests
`category:${prefs.favoriteCategories.join(',')}`
]
});
return results;
}
Email Personalization
// Dynamic content
function PersonalizedEmail({ user }: { user: User }) {
return (
<Html>
<Body>
<Text>Hi {user.name},</Text>
{user.lastPurchase && (
<Text>
Based on your purchase of {user.lastPurchase.product},
you might like these recommendations:
</Text>
)}
<ProductList products={getRecommendations(user)} />
</Body>
</Html>
);
}
Pattern 5: Analytics
Search Analytics
// Track search behavior
async function trackSearch(query: string, userId?: string) {
await analytics.track('Search Performed', {
query,
userId,
resultsCount: results.length,
timestamp: Date.now()
});
// Track no-results searches
if (results.length === 0) {
await analytics.track('Search No Results', {
query,
userId
});
}
}
Email Analytics
// Track email events
const webhookHandler = async (event: EmailEvent) => {
switch (event.type) {
case 'delivered':
await trackEmailDelivered(event);
break;
case 'opened':
await trackEmailOpened(event);
break;
case 'clicked':
await trackEmailClicked(event);
break;
case 'bounced':
await handleBounce(event);
break;
case 'complained':
await handleComplaint(event);
break;
}
};
Performance Benchmarks
| Operation | Latency | Throughput |
| Search Query | 10-50ms | 1000 req/s |
| Index Update | 100ms | 100 docs/s |
| Email Send | 200ms | 50 emails/s |
| Email Render | 50ms | 200/s |
Scaling Strategy
Search Sharding
// Distribute across shards
const shardId = hashUserId(userId) % NUM_SHARDS;
await searchClients[shardId].search(query);
Email Queues
// Queue emails for reliability
import { Queue } from 'bullmq';
const emailQueue = new Queue('emails', {
connection: redis
});
// Add to queue
await emailQueue.add('send', {
to: user.email,
template: 'welcome',
data: { name: user.name }
}, {
attempts: 3,
backoff: {
type: 'exponential',
delay: 1000
}
});
FAQ
Q1: Self-hosted vs managed search?
Managed for simplicity, self-hosted for control.
Q2: Email deliverability tips?
Warm up IPs, authenticate (SPF/DKIM/DMARC), monitor reputation.
Q3: Handle search typos?
Use fuzzy matching and phonetic algorithms.
Q4: Email design best practices?
Mobile-first, plain text alternative, test across clients.
Q5: Cost at scale?
Search: $100-500/month, Email: $0.10-0.50 per 1000 emails.
Production Checklist
Search
- [ ] Index schema designed
- [ ] Relevance tuned
- [ ] Filters implemented
- [ ] Analytics tracking
- [ ] Monitoring setup
- [ ] DNS records configured
- [ ] Templates designed
- [ ] Unsubscribe flow
- [ ] Bounce handling
- [ ] Spam monitoring
Conclusion
Search and email are infrastructure.
Key takeaways:
- Instant search is expected
- Email deliverability matters
- Track everything
- Personalize when possible
- Monitor continuously
Build discovery and communication that works.
Resources:
- Search Best Practices
- Email Deliverability Guide
- Template Libraries
- Analytics Setup
Next Steps:
- Set up search index
- Design email templates
- Configure DNS
- Implement tracking
- Monitor metrics
Improve discovery today.