Claude Code for Resend: Transactional Email with React — Claude Skills 360 Blog
Blog / Backend / Claude Code for Resend: Transactional Email with React
Backend

Claude Code for Resend: Transactional Email with React

Published: March 31, 2027
Read time: 7 min read
By: Claude Skills 360

Resend sends transactional email with a developer-friendly API — new Resend(apiKey) creates the client. resend.emails.send({ from, to, subject, react: <Template /> }) renders a React component to HTML and sends it. React Email provides building blocks: Html, Head, Body, Container, Section, Row, Column, Text, Button, Img, Hr, Link, and Preview. All styles must be inline objects since email clients strip <style> tags. resend.emails.send({ attachments: [{ filename, content }] }) handles file attachments. resend.batch.send([...]) sends multiple emails in one API call. Webhooks deliver email.sent, email.delivered, email.opened, and email.bounced events. react-email CLI with email dev server previews templates at localhost with live reload. Claude Code generates React Email templates, Resend API integration, welcome email sequences, order confirmation templates, and webhook handlers for email delivery tracking.

CLAUDE.md for Resend

## Resend Stack
- Version: resend >= 4.0, @react-email/components >= 0.0.21
- Client: const resend = new Resend(process.env.RESEND_API_KEY)
- Send: resend.emails.send({ from: "[email protected]", to, subject, react: <Email /> })
- Components: Html/Head/Body/Container/Section/Text/Button/Img/Hr/Link/Preview
- Styles: inline style objects only — email clients strip <style> tags
- Preview: add <Preview> text for inbox snippet (shows before subject is opened)
- Dev: npx react-email dev — preview at localhost:3000
- Batch: resend.batch.send(emailsArray) — up to 100 emails per call

Order Confirmation Email

// emails/OrderConfirmationEmail.tsx — React Email template
import {
  Html,
  Head,
  Preview,
  Body,
  Container,
  Section,
  Row,
  Column,
  Text,
  Button,
  Img,
  Hr,
  Link,
} from "@react-email/components"

interface OrderItem {
  name: string
  quantity: number
  priceCents: number
  imageUrl?: string
}

interface OrderConfirmationProps {
  customerName: string
  orderNumber: string
  orderDate: string
  items: OrderItem[]
  subtotalCents: number
  shippingCents: number
  totalCents: number
  trackingUrl?: string
  shippingAddress: {
    line1: string
    city: string
    state: string
    zip: string
  }
}

const fontFamily = "'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif"

export function OrderConfirmationEmail({
  customerName,
  orderNumber,
  orderDate,
  items,
  subtotalCents,
  shippingCents,
  totalCents,
  trackingUrl,
  shippingAddress,
}: OrderConfirmationProps) {
  const previewText = `Your order #${orderNumber} is confirmed — $${(totalCents / 100).toFixed(2)}`

  return (
    <Html lang="en">
      <Head />
      <Preview>{previewText}</Preview>
      <Body style={{ backgroundColor: "#f8fafc", fontFamily, margin: 0, padding: 0 }}>
        <Container style={{ maxWidth: 600, margin: "0 auto", padding: "40px 20px" }}>

          {/* Header */}
          <Section style={{ backgroundColor: "#ffffff", borderRadius: "12px 12px 0 0", padding: "32px 40px 24px" }}>
            <Text style={{ fontSize: 24, fontWeight: 700, color: "#0f172a", margin: 0 }}>
              Your Company
            </Text>
          </Section>

          {/* Hero */}
          <Section style={{ backgroundColor: "#3b82f6", padding: "24px 40px" }}>
            <Text style={{ fontSize: 22, fontWeight: 700, color: "#ffffff", margin: "0 0 4px" }}>
              Order Confirmed! 🎉
            </Text>
            <Text style={{ fontSize: 14, color: "#bfdbfe", margin: 0 }}>
              Hi {customerName}, we've received your order and it's being processed.
            </Text>
          </Section>

          {/* Order details */}
          <Section style={{ backgroundColor: "#ffffff", padding: "24px 40px" }}>
            <Row>
              <Column>
                <Text style={{ fontSize: 12, color: "#64748b", margin: "0 0 2px", textTransform: "uppercase" }}>
                  Order Number
                </Text>
                <Text style={{ fontSize: 16, fontWeight: 600, color: "#0f172a", margin: 0 }}>
                  #{orderNumber}
                </Text>
              </Column>
              <Column>
                <Text style={{ fontSize: 12, color: "#64748b", margin: "0 0 2px", textTransform: "uppercase" }}>
                  Order Date
                </Text>
                <Text style={{ fontSize: 16, fontWeight: 600, color: "#0f172a", margin: 0 }}>
                  {orderDate}
                </Text>
              </Column>
            </Row>
          </Section>

          <Hr style={{ borderColor: "#e2e8f0", margin: 0 }} />

          {/* Items */}
          <Section style={{ backgroundColor: "#ffffff", padding: "24px 40px" }}>
            <Text style={{ fontSize: 14, fontWeight: 600, color: "#64748b", textTransform: "uppercase", margin: "0 0 16px" }}>
              Items Ordered
            </Text>
            {items.map((item, i) => (
              <Row key={i} style={{ paddingBottom: 16 }}>
                <Column style={{ width: 48 }}>
                  {item.imageUrl && (
                    <Img
                      src={item.imageUrl}
                      alt={item.name}
                      width={40}
                      height={40}
                      style={{ borderRadius: 6, border: "1px solid #e2e8f0" }}
                    />
                  )}
                </Column>
                <Column style={{ paddingLeft: 12 }}>
                  <Text style={{ fontSize: 14, fontWeight: 500, color: "#0f172a", margin: "0 0 2px" }}>
                    {item.name}
                  </Text>
                  <Text style={{ fontSize: 12, color: "#64748b", margin: 0 }}>
                    Qty: {item.quantity}
                  </Text>
                </Column>
                <Column style={{ textAlign: "right" }}>
                  <Text style={{ fontSize: 14, fontWeight: 600, color: "#0f172a", margin: 0 }}>
                    ${((item.priceCents * item.quantity) / 100).toFixed(2)}
                  </Text>
                </Column>
              </Row>
            ))}

            <Hr style={{ borderColor: "#e2e8f0" }} />

            {/* Totals */}
            <Row style={{ marginBottom: 4 }}>
              <Column><Text style={{ fontSize: 13, color: "#64748b", margin: 0 }}>Subtotal</Text></Column>
              <Column style={{ textAlign: "right" }}>
                <Text style={{ fontSize: 13, color: "#0f172a", margin: 0 }}>
                  ${(subtotalCents / 100).toFixed(2)}
                </Text>
              </Column>
            </Row>
            <Row style={{ marginBottom: 4 }}>
              <Column><Text style={{ fontSize: 13, color: "#64748b", margin: 0 }}>Shipping</Text></Column>
              <Column style={{ textAlign: "right" }}>
                <Text style={{ fontSize: 13, color: "#0f172a", margin: 0 }}>
                  {shippingCents === 0 ? "Free" : `$${(shippingCents / 100).toFixed(2)}`}
                </Text>
              </Column>
            </Row>
            <Row>
              <Column><Text style={{ fontSize: 15, fontWeight: 700, color: "#0f172a", margin: "8px 0 0" }}>Total</Text></Column>
              <Column style={{ textAlign: "right" }}>
                <Text style={{ fontSize: 15, fontWeight: 700, color: "#3b82f6", margin: "8px 0 0" }}>
                  ${(totalCents / 100).toFixed(2)}
                </Text>
              </Column>
            </Row>
          </Section>

          <Hr style={{ borderColor: "#e2e8f0", margin: 0 }} />

          {/* Shipping address */}
          <Section style={{ backgroundColor: "#ffffff", padding: "20px 40px" }}>
            <Text style={{ fontSize: 12, color: "#64748b", textTransform: "uppercase", margin: "0 0 8px" }}>
              Shipping To
            </Text>
            <Text style={{ fontSize: 14, color: "#0f172a", lineHeight: "1.6", margin: 0 }}>
              {shippingAddress.line1}<br />
              {shippingAddress.city}, {shippingAddress.state} {shippingAddress.zip}
            </Text>
          </Section>

          {/* CTA */}
          <Section style={{ backgroundColor: "#ffffff", borderRadius: "0 0 12px 12px", padding: "16px 40px 32px", textAlign: "center" }}>
            {trackingUrl ? (
              <Button
                href={trackingUrl}
                style={{
                  backgroundColor: "#3b82f6",
                  color: "#ffffff",
                  padding: "12px 32px",
                  borderRadius: 8,
                  fontSize: 14,
                  fontWeight: 600,
                  textDecoration: "none",
                  display: "inline-block",
                }}
              >
                Track Your Order
              </Button>
            ) : (
              <Text style={{ fontSize: 13, color: "#64748b", margin: 0 }}>
                You'll receive a shipping confirmation when your order ships.
              </Text>
            )}
          </Section>

          {/* Footer */}
          <Section style={{ padding: "24px 0 0", textAlign: "center" }}>
            <Text style={{ fontSize: 12, color: "#94a3b8", margin: "0 0 8px" }}>
              Questions? <Link href="mailto:[email protected]" style={{ color: "#3b82f6" }}>Contact Support</Link>
            </Text>
            <Text style={{ fontSize: 11, color: "#cbd5e1", margin: 0 }}>
              © 2024 Your Company Inc. · 123 Main St, San Francisco, CA 94105
            </Text>
          </Section>

        </Container>
      </Body>
    </Html>
  )
}

export default OrderConfirmationEmail

Resend Send Integration

// lib/email.ts — email sending service
import { Resend } from "resend"
import { OrderConfirmationEmail } from "@/emails/OrderConfirmationEmail"
import { WelcomeEmail } from "@/emails/WelcomeEmail"
import { PasswordResetEmail } from "@/emails/PasswordResetEmail"

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

const FROM = "Your Company <[email protected]>"

export async function sendOrderConfirmation(params: {
  to: string
  customerName: string
  order: {
    number: string
    date: string
    items: Parameters<typeof OrderConfirmationEmail>[0]["items"]
    subtotalCents: number
    shippingCents: number
    totalCents: number
    trackingUrl?: string
    shippingAddress: Parameters<typeof OrderConfirmationEmail>[0]["shippingAddress"]
  }
}) {
  const { data, error } = await resend.emails.send({
    from: FROM,
    to: params.to,
    subject: `Order Confirmed — #${params.order.number}`,
    react: OrderConfirmationEmail({
      customerName: params.customerName,
      orderNumber: params.order.number,
      orderDate: params.order.date,
      items: params.order.items,
      subtotalCents: params.order.subtotalCents,
      shippingCents: params.order.shippingCents,
      totalCents: params.order.totalCents,
      trackingUrl: params.order.trackingUrl,
      shippingAddress: params.order.shippingAddress,
    }),
  })

  if (error) throw new Error(`Failed to send order confirmation: ${error.message}`)
  return data
}

export async function sendBulkAnnouncement(
  recipients: { email: string; name: string }[],
  announcement: { subject: string; body: string }
) {
  const emails = recipients.map(r => ({
    from: FROM,
    to: r.email,
    subject: announcement.subject,
    html: `<p>Hi ${r.name},</p><p>${announcement.body}</p>`,
  }))

  // Batch up to 100 at a time
  const results = []
  for (let i = 0; i < emails.length; i += 100) {
    const batch = emails.slice(i, i + 100)
    const { data, error } = await resend.batch.send(batch)
    if (error) console.error(`Batch ${i / 100} failed:`, error)
    if (data) results.push(...data)
  }
  return results
}

For the SendGrid alternative when high-volume transactional email, advanced suppression lists, IP warming, and dedicated IP addresses are required — SendGrid has enterprise deliverability features but requires HTML string templates rather than React components, see the email platform comparison for volume-scale sending. For the Postmark alternative when specialized transactional email with the highest delivery speeds, a strict no-bulk-email policy, and detailed message streams for transactional vs broadcast separation are needed — Postmark has an excellent deliverability reputation and message stream architecture, see the transactional email guide. The Claude Skills 360 bundle includes Resend skill sets covering React Email templates, order confirmations, and batch sending. Start with the free tier to try email generation.

Keep Reading

Backend

Claude Code for Bun: Fast JavaScript Runtime and Toolkit

Build with Bun and Claude Code — Bun.serve for HTTP servers, Bun.file for fast file I/O, Bun.$ for shell commands, Bun.sql for SQLite and PostgreSQL, Bun.build for bundling, bun:test for testing, Bun.hash for hashing, bun.lock for deterministic installs, bun run for package.json scripts, hot reloading with --hot, bun init for project scaffolding, and compatibility with Node.js modules.

6 min read Jun 13, 2027
Backend

Claude Code for Express.js Advanced: Patterns for Production APIs

Advanced Express.js patterns with Claude Code — typed request handlers with RequestHandler generics, async error handling middleware, Zod validation middleware factory, rate limiting with express-rate-limit and Redis store, helmet security middleware, compression, dependency injection with tsyringe, file upload with multer and S3, pagination utilities, JWT middleware, and structured logging with pino.

6 min read Jun 8, 2027
Backend

Claude Code for KeystoneJS: Node.js CMS and App Framework

Build full-stack apps with KeystoneJS and Claude Code — config with lists, fields.text and fields.relationship for schema definition, access control with isAuthenticated and isAdmin functions, hooks with beforeOperation and afterOperation, GraphQL API auto-generation from schema, AdminUI for content management, session with statelessSessions, Prisma adapter for database, file storage with images and files fields, and custom REST endpoints.

6 min read Jun 7, 2027

Put these ideas into practice

Claude Skills 360 gives you production-ready skills for everything in this article — and 2,350+ more. Start free or go all-in.

Back to Blog

Get 360 skills free