Componentes de React Email

@react-email/components incluye todos los componentes que necesitas para construir emails compatibles con cualquier cliente. Cada componente genera HTML optimizado con tablas y estilos inline por debajo, pero tú solo escribes JSX limpio.

Componentes de estructura

Estos componentes son la base de cualquier template:

Html, Head y Body

Obligatorios en todo template. Son el equivalente a <html>, <head> y <body>:

tsx
import { Html, Head, Body } from '@react-email/components'
 
export function MiEmail() {
  return (
    <Html>
      <Head />
      <Body style={{ backgroundColor: '#ffffff' }}>
        {/* Contenido */}
      </Body>
    </Html>
  )
}

Container

Un div centrado con max-width. Funciona como el wrapper principal de tu contenido:

tsx
<Container style={{ maxWidth: '600px', margin: '0 auto', padding: '20px' }}>
  {/* Contenido centrado */}
</Container>

Section

Agrupa contenido dentro del Container. Por debajo usa display: table para garantizar compatibilidad con clientes de email que no soportan flexbox:

tsx
<Section style={{ padding: '24px 0' }}>
  <Text>Sección de contenido</Text>
</Section>

Row y Column

Layout en columnas. Es el equivalente a un grid o flexbox pero compatible con email:

tsx
<Row>
  <Column style={{ width: '50%' }}>
    <Text>Columna izquierda</Text>
  </Column>
  <Column style={{ width: '50%' }}>
    <Text>Columna derecha</Text>
  </Column>
</Row>

Componentes de contenido

Text

Párrafos de texto. Acepta cualquier estilo inline:

tsx
<Text style={{ fontSize: '16px', color: '#333' }}>
  Este es un párrafo normal.
</Text>

Heading

Encabezados de h1 a h6. Usa la prop as para definir el nivel:

tsx
<Heading as="h2" style={{ fontSize: '20px', color: '#111' }}>
  Título de sección
</Heading>

Enlaces <a> con estilos:

tsx
<Link href="https://miapp.com/dashboard" style={{ color: '#0070f3' }}>
  Ir al dashboard
</Link>

Button

Un botón-enlace con estilos. Por debajo genera un <a> estilizado, no un <button> (los botones HTML no funcionan en emails):

tsx
<Button
  href="https://miapp.com/confirmar"
  style={{
    backgroundColor: '#000',
    color: '#fff',
    padding: '12px 24px',
    borderRadius: '6px',
    textDecoration: 'none',
  }}
>
  Confirmar cuenta
</Button>

Img

Imágenes con ancho y alto explicitos (importante para que no se deformen en diferentes clientes):

tsx
<Img
  src="https://miapp.com/logo.png"
  alt="Logo de Mi App"
  width={150}
  height={40}
/>

Hr

Línea separadora:

tsx
<Hr style={{ borderColor: '#e6e6e6', margin: '20px 0' }} />

Preview

Preview mejora la tasa de apertura

El componente Preview define el texto que aparece en la bandeja de entrada junto al asunto, antes de que el usuario abra el email. Es lo primero que ven tus usuarios, así que úsalo para dar contexto y aumentar engagement.

tsx
<Preview>Tu pedido #1234 fue enviado -- revisa los detalles</Preview>

El texto de Preview no se muestra dentro del email, solo en la vista previa de la bandeja de entrada.

Font

Carga fuentes web. Ten en cuenta que solo funciona en algunos clientes (Apple Mail, iOS Mail). Gmail y Outlook usan fuentes del sistema:

tsx
<Font
  fontFamily="Inter"
  fallbackFontFamily="Arial"
  webFont={{
    url: 'https://fonts.googleapis.com/css2?family=Inter',
    format: 'woff2',
  }}
/>

Ejemplo: template de factura

tsx
import {
  Html,
  Head,
  Body,
  Container,
  Section,
  Row,
  Column,
  Text,
  Heading,
  Hr,
  Preview,
} from '@react-email/components'
 
interface InvoiceEmailProps {
  nombreCliente: string
  numeroFactura: string
  items: { descripcion: string; cantidad: number; precio: number }[]
  total: number
}
 
export function InvoiceEmail({
  nombreCliente,
  numeroFactura,
  items,
  total,
}: InvoiceEmailProps) {
  return (
    <Html>
      <Head />
      <Preview>Factura #{numeroFactura} -- Total: ${total} MXN</Preview>
      <Body style={{ backgroundColor: '#f6f9fc', fontFamily: 'sans-serif' }}>
        <Container style={{ maxWidth: '600px', margin: '0 auto', padding: '20px' }}>
          <Heading as="h1" style={{ fontSize: '24px', color: '#111' }}>
            Factura #{numeroFactura}
          </Heading>
          <Text style={{ color: '#666' }}>Hola {nombreCliente},</Text>
          <Text>Aquí tienes el detalle de tu compra:</Text>
 
          <Section style={{ margin: '20px 0' }}>
            <Row style={{ borderBottom: '1px solid #e6e6e6', padding: '8px 0' }}>
              <Column style={{ width: '50%' }}>
                <Text style={{ fontWeight: 'bold', margin: '0' }}>Descripción</Text>
              </Column>
              <Column style={{ width: '25%' }}>
                <Text style={{ fontWeight: 'bold', margin: '0' }}>Cantidad</Text>
              </Column>
              <Column style={{ width: '25%' }}>
                <Text style={{ fontWeight: 'bold', margin: '0' }}>Precio</Text>
              </Column>
            </Row>
            {items.map((item, i) => (
              <Row key={i} style={{ borderBottom: '1px solid #f0f0f0', padding: '8px 0' }}>
                <Column style={{ width: '50%' }}>
                  <Text style={{ margin: '0' }}>{item.descripcion}</Text>
                </Column>
                <Column style={{ width: '25%' }}>
                  <Text style={{ margin: '0' }}>{item.cantidad}</Text>
                </Column>
                <Column style={{ width: '25%' }}>
                  <Text style={{ margin: '0' }}>${item.precio} MXN</Text>
                </Column>
              </Row>
            ))}
          </Section>
 
          <Hr />
          <Text style={{ fontSize: '18px', fontWeight: 'bold' }}>
            Total: ${total} MXN
          </Text>
        </Container>
      </Body>
    </Html>
  )
}

Ejemplo: reset de contraseña

tsx
import {
  Html,
  Head,
  Body,
  Container,
  Text,
  Button,
  Hr,
  Preview,
} from '@react-email/components'
 
interface ResetPasswordProps {
  nombre: string
  resetUrl: string
}
 
export function ResetPasswordEmail({ nombre, resetUrl }: ResetPasswordProps) {
  return (
    <Html>
      <Head />
      <Preview>Restablece tu contraseña</Preview>
      <Body style={{ backgroundColor: '#f6f9fc', fontFamily: 'sans-serif' }}>
        <Container style={{ maxWidth: '600px', margin: '0 auto', padding: '20px' }}>
          <Text style={{ fontSize: '20px', fontWeight: 'bold' }}>
            Hola {nombre}
          </Text>
          <Text>
            Recibimos una solicitud para restablecer la contraseña de tu cuenta.
            Si no fuiste tú, ignora este email.
          </Text>
          <Button
            href={resetUrl}
            style={{
              backgroundColor: '#000',
              color: '#fff',
              padding: '12px 24px',
              borderRadius: '6px',
            }}
          >
            Restablecer contraseña
          </Button>
          <Hr />
          <Text style={{ color: '#666', fontSize: '12px' }}>
            Este enlace expira en 1 hora. Si no solicitaste un cambio de
            contraseña, puedes ignorar este email.
          </Text>
        </Container>
      </Body>
    </Html>
  )
}

Ejemplo: notificación simple

tsx
import {
  Html,
  Head,
  Body,
  Container,
  Text,
  Link,
  Preview,
} from '@react-email/components'
 
interface NotificationProps {
  mensaje: string
  actionUrl: string
  actionLabel: string
}
 
export function NotificationEmail({
  mensaje,
  actionUrl,
  actionLabel,
}: NotificationProps) {
  return (
    <Html>
      <Head />
      <Preview>{mensaje}</Preview>
      <Body style={{ backgroundColor: '#f6f9fc', fontFamily: 'sans-serif' }}>
        <Container style={{ maxWidth: '600px', margin: '0 auto', padding: '20px' }}>
          <Text style={{ fontSize: '16px', color: '#333' }}>{mensaje}</Text>
          <Link
            href={actionUrl}
            style={{ color: '#0070f3', textDecoration: 'underline' }}
          >
            {actionLabel}
          </Link>
        </Container>
      </Body>
    </Html>
  )
}
Estilos siempre como objetos inline

Todos los estilos en React Email deben ser objetos inline de React (la prop style con un objeto JavaScript). No uses strings CSS ni clases. Es la única forma de garantizar que los estilos funcionen en todos los clientes de email.