Master Resend email sending patterns for Google Antigravity IDE applications
# Resend Email Patterns for Google Antigravity IDE
Build beautiful transactional emails with Resend using Google Antigravity IDE's Gemini 3 assistance. This guide covers React Email templates, sending patterns, webhooks, and batch operations for production email systems.
## Core Setup
```typescript
// src/lib/resend.ts
import { Resend } from "resend";
export const resend = new Resend(process.env.RESEND_API_KEY);
// Email sending wrapper with error handling
export async function sendEmail<T extends React.ComponentType<any>>({
to,
subject,
template: Template,
props,
from = "noreply@yourdomain.com",
replyTo,
cc,
bcc,
headers,
tags,
}: {
to: string | string[];
subject: string;
template: T;
props: React.ComponentProps<T>;
from?: string;
replyTo?: string;
cc?: string | string[];
bcc?: string | string[];
headers?: Record<string, string>;
tags?: Array<{ name: string; value: string }>;
}) {
try {
const { data, error } = await resend.emails.send({
from,
to: Array.isArray(to) ? to : [to],
subject,
react: <Template {...props} />,
replyTo,
cc: cc ? (Array.isArray(cc) ? cc : [cc]) : undefined,
bcc: bcc ? (Array.isArray(bcc) ? bcc : [bcc]) : undefined,
headers,
tags,
});
if (error) {
console.error("Email send error:", error);
throw new Error(error.message);
}
return { success: true, id: data?.id };
} catch (error) {
console.error("Email send failed:", error);
throw error;
}
}
```
## React Email Templates
```typescript
// emails/WelcomeEmail.tsx
import {
Body,
Button,
Container,
Head,
Heading,
Hr,
Html,
Img,
Link,
Preview,
Section,
Text,
Tailwind,
} from "@react-email/components";
interface WelcomeEmailProps {
name: string;
verificationUrl: string;
features: string[];
}
export default function WelcomeEmail({
name,
verificationUrl,
features,
}: WelcomeEmailProps) {
return (
<Html>
<Head />
<Preview>Welcome to our platform, {name}!</Preview>
<Tailwind>
<Body className="bg-gray-100 font-sans">
<Container className="bg-white mx-auto p-8 rounded-lg shadow-lg max-w-xl">
<Img
src="https://yourdomain.com/logo.png"
width={150}
height={50}
alt="Logo"
className="mx-auto mb-6"
/>
<Heading className="text-2xl font-bold text-gray-900 text-center mb-4">
Welcome, {name}!
</Heading>
<Text className="text-gray-600 text-base leading-6 mb-6">
We're excited to have you on board. Before you get started,
please verify your email address by clicking the button below.
</Text>
<Section className="text-center mb-8">
<Button
href={verificationUrl}
className="bg-blue-600 text-white font-semibold py-3 px-6 rounded-lg"
>
Verify Email Address
</Button>
</Section>
<Hr className="border-gray-200 my-6" />
<Text className="text-gray-600 text-sm mb-4">
Here's what you can do with your account:
</Text>
<ul className="text-gray-600 text-sm mb-6">
{features.map((feature, index) => (
<li key={index} className="mb-2">
✓ {feature}
</li>
))}
</ul>
<Text className="text-gray-500 text-xs text-center">
If you didn't create an account, you can safely ignore this email.
</Text>
<Hr className="border-gray-200 my-6" />
<Text className="text-gray-400 text-xs text-center">
© 2024 Your Company. All rights reserved.
<br />
<Link href="https://yourdomain.com/unsubscribe" className="text-blue-500">
Unsubscribe
</Link>
</Text>
</Container>
</Body>
</Tailwind>
</Html>
);
}
```
## Batch Sending
```typescript
// src/services/email-campaigns.ts
import { resend } from "@/lib/resend";
import { db } from "@/lib/db";
import NewsletterEmail from "@/emails/NewsletterEmail";
export async function sendNewsletter(
campaignId: string,
options?: { batchSize?: number; delayMs?: number }
) {
const { batchSize = 100, delayMs = 1000 } = options ?? {};
const campaign = await db.campaign.findUnique({
where: { id: campaignId },
include: { subscribers: true },
});
if (!campaign) throw new Error("Campaign not found");
const subscribers = campaign.subscribers.filter((s) => s.status === "active");
const batches = chunk(subscribers, batchSize);
const results: Array<{ batch: number; sent: number; failed: number }> = [];
for (let i = 0; i < batches.length; i++) {
const batch = batches[i];
const emails = batch.map((subscriber) => ({
from: "newsletter@yourdomain.com",
to: subscriber.email,
subject: campaign.subject,
react: (
<NewsletterEmail
content={campaign.content}
subscriberName={subscriber.name}
unsubscribeUrl={`https://yourdomain.com/unsubscribe?token=${subscriber.unsubscribeToken}`}
/>
),
tags: [
{ name: "campaign_id", value: campaignId },
{ name: "subscriber_id", value: subscriber.id },
],
}));
const { data, error } = await resend.batch.send(emails);
results.push({
batch: i + 1,
sent: data?.length ?? 0,
failed: error ? batch.length : 0,
});
// Rate limiting delay between batches
if (i < batches.length - 1) {
await new Promise((resolve) => setTimeout(resolve, delayMs));
}
}
// Update campaign stats
await db.campaign.update({
where: { id: campaignId },
data: {
sentAt: new Date(),
sentCount: results.reduce((sum, r) => sum + r.sent, 0),
failedCount: results.reduce((sum, r) => sum + r.failed, 0),
},
});
return results;
}
function chunk<T>(array: T[], size: number): T[][] {
return Array.from({ length: Math.ceil(array.length / size) }, (_, i) =>
array.slice(i * size, i * size + size)
);
}
```
## Webhook Handler
```typescript
// src/app/api/webhooks/resend/route.ts
import { NextRequest, NextResponse } from "next/server";
import { Webhook } from "svix";
import { db } from "@/lib/db";
const webhookSecret = process.env.RESEND_WEBHOOK_SECRET!;
export async function POST(request: NextRequest) {
const payload = await request.text();
const headers = {
"svix-id": request.headers.get("svix-id") ?? "",
"svix-timestamp": request.headers.get("svix-timestamp") ?? "",
"svix-signature": request.headers.get("svix-signature") ?? "",
};
const wh = new Webhook(webhookSecret);
let event: { type: string; data: any };
try {
event = wh.verify(payload, headers) as { type: string; data: any };
} catch {
return NextResponse.json({ error: "Invalid signature" }, { status: 401 });
}
switch (event.type) {
case "email.sent":
await db.emailLog.update({
where: { resendId: event.data.email_id },
data: { status: "sent", sentAt: new Date() },
});
break;
case "email.delivered":
await db.emailLog.update({
where: { resendId: event.data.email_id },
data: { status: "delivered", deliveredAt: new Date() },
});
break;
case "email.bounced":
await handleBounce(event.data);
break;
case "email.complained":
await handleComplaint(event.data);
break;
}
return NextResponse.json({ received: true });
}
```
## Best Practices for Google Antigravity IDE
When using Resend with Google Antigravity, create reusable React Email components. Implement proper error handling and retries. Use batch sending for large campaigns. Handle webhooks for delivery tracking. Add unsubscribe links for compliance. Let Gemini 3 generate email templates from your design requirements.
Google Antigravity excels at creating beautiful, responsive email templates with React Email.This Resend prompt is ideal for developers working on:
By using this prompt, you can save hours of manual coding and ensure best practices are followed from the start. It's particularly valuable for teams looking to maintain consistency across their resend implementations.
Yes! All prompts on Antigravity AI Directory are free to use for both personal and commercial projects. No attribution required, though it's always appreciated.
This prompt works excellently with Claude, ChatGPT, Cursor, GitHub Copilot, and other modern AI coding assistants. For best results, use models with large context windows.
You can modify the prompt by adding specific requirements, constraints, or preferences. For Resend projects, consider mentioning your framework version, coding style, and any specific libraries you're using.