Skip to content

Fix: React Email and Resend Not Working — Emails Not Sending, Templates Not Rendering, or Styling Broken

FixDevs ·

Quick Answer

How to fix React Email and Resend issues — email template components, inline styles, Resend API integration, preview server, attachments, and email client compatibility.

The Problem

Resend returns a success response but no email arrives:

import { Resend } from 'resend';

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

const { data, error } = await resend.emails.send({
  from: '[email protected]',
  to: '[email protected]',
  subject: 'Welcome!',
  html: '<h1>Hello</h1>',
});
// data.id exists but email never arrives

Or React Email components render in the preview but look broken in actual email clients:

import { Html, Body, Container, Text } from '@react-email/components';

function WelcomeEmail() {
  return (
    <Html>
      <Body style={{ display: 'flex', justifyContent: 'center' }}>
        <Container>
          <Text>Welcome!</Text>
        </Container>
      </Body>
    </Html>
  );
}
// Preview looks perfect — Gmail shows a mess

Or the React Email preview server fails:

npx react-email dev
# Error: Cannot find module '@react-email/components'

Why This Happens

React Email renders React components to HTML strings for email. Resend is the sending API. The issues are at different layers:

  • Resend requires a verified domain — the from address must use a domain you’ve verified in the Resend dashboard. Using an unverified domain silently fails or goes to spam. [email protected] is the only default sender that works without verification.
  • Email clients strip most CSS — Gmail removes <style> tags, Outlook doesn’t support flexbox or grid, and Apple Mail handles media queries differently. React Email components use inline styles and table-based layouts specifically because those are the only reliable approaches across email clients.
  • React Email is server-side onlyrender() converts React Email components to HTML strings. This runs on the server (API route, Server Action). Trying to import React Email components in client-side code fails because some dependencies are Node.js-only.
  • The preview server needs the components installednpx react-email dev scans for .tsx files in your emails/ directory. If the directory structure or component imports are wrong, the preview server shows nothing.

Fix 1: Set Up React Email Templates

npm install @react-email/components react-email resend
// emails/welcome.tsx — email template
import {
  Html,
  Head,
  Preview,
  Body,
  Container,
  Section,
  Text,
  Link,
  Img,
  Button,
  Hr,
  Row,
  Column,
} from '@react-email/components';

interface WelcomeEmailProps {
  username: string;
  loginUrl: string;
}

export default function WelcomeEmail({ username, loginUrl }: WelcomeEmailProps) {
  return (
    <Html>
      <Head />
      {/* Preview text — shown in inbox list, not in email body */}
      <Preview>Welcome to MyApp, {username}!</Preview>
      <Body style={main}>
        <Container style={container}>
          {/* Logo */}
          <Img
            src="https://myapp.com/logo.png"
            width="120"
            height="36"
            alt="MyApp"
            style={{ margin: '0 auto', display: 'block' }}
          />

          <Text style={heading}>Welcome, {username}!</Text>

          <Text style={paragraph}>
            Your account has been created. Click the button below to get started.
          </Text>

          {/* CTA button — must use table-based layout for Outlook */}
          <Section style={{ textAlign: 'center' as const, marginTop: '32px' }}>
            <Button
              href={loginUrl}
              style={button}
            >
              Go to Dashboard
            </Button>
          </Section>

          <Hr style={hr} />

          <Text style={footer}>
            If you didn't create this account, you can safely ignore this email.
          </Text>
        </Container>
      </Body>
    </Html>
  );
}

// Styles must be inline objects — no CSS classes, no external stylesheets
const main = {
  backgroundColor: '#f6f9fc',
  fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
};

const container = {
  backgroundColor: '#ffffff',
  margin: '0 auto',
  padding: '40px 20px',
  maxWidth: '560px',
  borderRadius: '8px',
};

const heading = {
  fontSize: '24px',
  fontWeight: '600' as const,
  color: '#1a1a1a',
  textAlign: 'center' as const,
  margin: '30px 0',
};

const paragraph = {
  fontSize: '16px',
  lineHeight: '26px',
  color: '#484848',
};

const button = {
  backgroundColor: '#3b82f6',
  borderRadius: '6px',
  color: '#fff',
  fontSize: '16px',
  fontWeight: '600' as const,
  textDecoration: 'none',
  textAlign: 'center' as const,
  display: 'inline-block',
  padding: '12px 24px',
};

const hr = {
  borderColor: '#e6ebf1',
  margin: '32px 0',
};

const footer = {
  color: '#8898aa',
  fontSize: '12px',
  lineHeight: '16px',
};

Fix 2: Send Emails with Resend

# Set up environment variable
# RESEND_API_KEY=re_xxxxxxxxxx
// lib/email.ts — Resend client
import { Resend } from 'resend';
import { render } from '@react-email/render';
import WelcomeEmail from '@/emails/welcome';

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

export async function sendWelcomeEmail(to: string, username: string) {
  // Render React component to HTML string
  const html = await render(WelcomeEmail({
    username,
    loginUrl: `https://myapp.com/login`,
  }));

  const { data, error } = await resend.emails.send({
    from: 'MyApp <[email protected]>',  // Must be verified domain
    to,
    subject: `Welcome to MyApp, ${username}!`,
    html,
    // Optional: plain text fallback
    text: `Welcome to MyApp, ${username}! Visit https://myapp.com/login to get started.`,
    // Optional: reply-to
    replyTo: '[email protected]',
    // Optional: tags for analytics
    tags: [
      { name: 'category', value: 'welcome' },
    ],
  });

  if (error) {
    console.error('Email send failed:', error);
    throw new Error(`Failed to send email: ${error.message}`);
  }

  return data;
}

// Or pass the React component directly (Resend supports this)
export async function sendWelcomeEmailDirect(to: string, username: string) {
  const { data, error } = await resend.emails.send({
    from: 'MyApp <[email protected]>',
    to,
    subject: `Welcome to MyApp, ${username}!`,
    react: WelcomeEmail({ username, loginUrl: 'https://myapp.com/login' }),
  });

  return { data, error };
}
// app/api/send/route.ts — Next.js API route
import { sendWelcomeEmail } from '@/lib/email';

export async function POST(request: Request) {
  const { email, username } = await request.json();

  try {
    const result = await sendWelcomeEmail(email, username);
    return Response.json({ success: true, id: result?.id });
  } catch (error) {
    return Response.json({ error: 'Failed to send email' }, { status: 500 });
  }
}

Fix 3: Email-Safe Styling

Email clients have strict CSS support. Use these patterns:

import {
  Html, Body, Container, Section, Row, Column, Text, Img,
} from '@react-email/components';

// Two-column layout — use Row/Column (renders as tables)
function NewsletterEmail({ articles }: { articles: Article[] }) {
  return (
    <Html>
      <Body style={{ backgroundColor: '#f6f9fc', fontFamily: 'Arial, sans-serif' }}>
        <Container style={{ maxWidth: '600px', margin: '0 auto' }}>
          {/* Header with background color */}
          <Section style={{
            backgroundColor: '#1a1a2e',
            padding: '40px 20px',
            textAlign: 'center' as const,
            borderRadius: '8px 8px 0 0',
          }}>
            <Text style={{ color: '#ffffff', fontSize: '28px', fontWeight: 'bold', margin: '0' }}>
              Weekly Digest
            </Text>
          </Section>

          {/* Two-column layout */}
          <Section style={{ padding: '20px' }}>
            <Row>
              <Column style={{ width: '50%', paddingRight: '10px', verticalAlign: 'top' }}>
                <Text style={{ fontWeight: 'bold', fontSize: '16px' }}>
                  {articles[0]?.title}
                </Text>
                <Text style={{ color: '#666', fontSize: '14px' }}>
                  {articles[0]?.excerpt}
                </Text>
              </Column>
              <Column style={{ width: '50%', paddingLeft: '10px', verticalAlign: 'top' }}>
                <Text style={{ fontWeight: 'bold', fontSize: '16px' }}>
                  {articles[1]?.title}
                </Text>
                <Text style={{ color: '#666', fontSize: '14px' }}>
                  {articles[1]?.excerpt}
                </Text>
              </Column>
            </Row>
          </Section>

          {/* Safe CSS properties for email */}
          {/*
            ✅ Safe: color, background-color, font-size, font-weight, font-family,
               text-align, padding, margin, border, width, height, line-height,
               text-decoration, vertical-align, display (block/inline/none)

            ❌ Avoid: flexbox, grid, position, float, box-shadow, border-radius (Outlook),
               transform, animation, CSS variables, calc(), media queries (limited)
          */}
        </Container>
      </Body>
    </Html>
  );
}

Fix 4: Resend Domain Verification

# Verify domain in Resend dashboard:
# 1. Go to https://resend.com/domains
# 2. Add your domain (e.g., myapp.com)
# 3. Add the DNS records (SPF, DKIM, DMARC) to your domain provider
# 4. Wait for verification (usually minutes, sometimes hours)

DNS records you’ll need to add:

TypeNameValuePurpose
TXT@ or myapp.comv=spf1 include:amazonses.com ~allSPF
CNAMEresend._domainkeyProvided by ResendDKIM
TXT_dmarcv=DMARC1; p=none;DMARC

Until domain is verified, use the test sender:

await resend.emails.send({
  from: 'Acme <[email protected]>',  // Works without verification
  to: '[email protected]',             // Test recipient
  subject: 'Test',
  html: '<p>Test email</p>',
});

Fix 5: Preview and Development

# Start the React Email preview server
npx react-email dev

# Or add to package.json
# "email:dev": "react-email dev --dir emails --port 3001"
# Expected directory structure
emails/
├── welcome.tsx
├── reset-password.tsx
├── order-confirmation.tsx
└── newsletter.tsx
// Generate static HTML for testing
import { render } from '@react-email/render';
import WelcomeEmail from './emails/welcome';

const html = await render(WelcomeEmail({
  username: 'Alice',
  loginUrl: 'https://myapp.com/login',
}));

// Write to file for manual testing
import fs from 'fs';
fs.writeFileSync('test-email.html', html);
// Open test-email.html in a browser to preview

Fix 6: Attachments and Batch Sending

import { Resend } from 'resend';
import fs from 'fs';

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

// Send with attachment
await resend.emails.send({
  from: 'MyApp <[email protected]>',
  to: '[email protected]',
  subject: 'Your Invoice',
  html: '<p>Please find your invoice attached.</p>',
  attachments: [
    {
      filename: 'invoice.pdf',
      content: fs.readFileSync('./invoices/inv-001.pdf'),
    },
    {
      filename: 'receipt.pdf',
      path: 'https://myapp.com/receipts/rec-001.pdf',  // URL-based
    },
  ],
});

// Batch send — multiple emails in one API call
const { data, error } = await resend.batch.send([
  {
    from: 'MyApp <[email protected]>',
    to: '[email protected]',
    subject: 'Hello Alice',
    html: '<p>Hi Alice!</p>',
  },
  {
    from: 'MyApp <[email protected]>',
    to: '[email protected]',
    subject: 'Hello Bob',
    html: '<p>Hi Bob!</p>',
  },
]);
// Up to 100 emails per batch

// Schedule email for later
await resend.emails.send({
  from: 'MyApp <[email protected]>',
  to: '[email protected]',
  subject: 'Reminder',
  html: '<p>Don\'t forget!</p>',
  scheduledAt: '2024-12-25T09:00:00Z',  // ISO 8601
});

Still Not Working?

Email sends successfully but never arrives — check spam/junk folder first. Then verify your domain’s DNS records are correct in the Resend dashboard (all three: SPF, DKIM, DMARC should show green checkmarks). Without proper DNS, emails are flagged as spam or silently dropped by the recipient’s mail server.

React Email components render as plain text — you’re passing the component as a string instead of rendering it. Use render(MyEmail(props)) to convert the React component to an HTML string, or pass the component directly via Resend’s react property instead of html.

Styles look correct in preview but broken in Gmail — Gmail strips <style> tags and class attributes. All styles must be inline. React Email’s <style> component handles some inlining, but manual inline styles are most reliable. Also, Gmail doesn’t support border-radius in some contexts, background-image, or CSS variables.

render() throws “Cannot read properties of undefined” — the component might be using hooks or browser APIs. React Email templates must be pure render functions — no useState, no useEffect, no window. They’re rendered once on the server to produce a static HTML string.

For related API and backend issues, see Fix: Auth.js Not Working and Fix: Hono Not Working.

F

FixDevs

Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.

Was this article helpful?

Related Articles