Raypx

@raypx/email

Email sending with Resend and React Email templates.

@raypx/email provides email sending capabilities with pluggable transports and React Email templates. It separates the rendering of email templates from the transport mechanism, allowing the same template to be sent via Resend in production or logged to the console in development.

Transports

Three transports are available:

TransportUse Case
createResendTransport(options)Production -- sends emails via Resend
createLoggerTransport(options?)Development -- logs email payloads using @raypx/logger
createNoopTransport()Testing -- silently accepts all emails

Resend Transport

The primary transport for production use:

import { createResendTransport } from "@raypx/email/transports"

const transport = createResendTransport({
  apiKey: "re_...",
  fromEmail: "noreply@example.com",
  fromName: "Raypx",
})

A pre-configured resendTransport singleton is also exported when the RESEND_API_KEY environment variable is set.

Logger Transport

Logs email details to the console via @raypx/logger instead of sending:

import { createLoggerTransport } from "@raypx/email/transports"

const transport = createLoggerTransport({ feature: "auth" })
// Logs: email.send { to: "user@example.com", subject: "Verify your email" }

Noop Transport

Does nothing. Useful in test environments:

import { createNoopTransport } from "@raypx/email/transports"

const transport = createNoopTransport()

Templates

Built-in templates are provided for common email flows:

TemplateFunctionDescription
emailVerificationTemplaterenderEmailVerificationEmail()Email address verification on sign-up
passwordResetTemplaterenderPasswordResetEmail()Password reset link
otpTemplate--One-time password delivery

Using Templates

Templates are React Email components that return { subject, html, text }:

import { renderEmailVerificationEmail } from "@raypx/email/templates"
import { sendMail, createSender } from "@raypx/email"

const { subject, html, text } = renderEmailVerificationEmail({
  email: "user@example.com",
  name: "John",
  url: "https://example.com/verify?token=abc",
})

await sendMail(
  { from: { email: "noreply@example.com" }, to: { email: "user@example.com" }, subject, html, text },
  { transport },
)

Rendering

Two rendering functions are available:

import { renderTemplate, renderTemplateAsync } from "@raypx/email"

// Synchronous or async depending on template
const result = renderTemplate(myTemplate, input)

// Always returns a Promise with resolved HTML
const result = await renderTemplateAsync(myTemplate, input)

renderTemplateAsync is the safe choice -- it handles both sync and async templates and ensures the html field is fully resolved before returning.

Creating Custom Templates

Create a template function that matches the EmailTemplate<TInput> or AsyncEmailTemplate<TInput> type:

import type { EmailTemplate } from "@raypx/email"

type WelcomeInput = { name: string; email: string }

const welcomeTemplate: EmailTemplate<WelcomeInput> = ({ name, email }) => ({
  subject: `Welcome to Raypx, ${name}!`,
  html: `<h1>Welcome, ${name}!</h1><p>Your account has been created.</p>`,
  text: `Welcome, ${name}! Your account has been created.`,
})

For React Email templates with async rendering (e.g., using renderAsync):

import type { AsyncEmailTemplate } from "@raypx/email"
import { renderAsync } from "@react-email/render"

const richTemplate: AsyncEmailTemplate<{ name: string }> = async ({ name }) => ({
  subject: "Welcome",
  html: await renderAsync(<WelcomeEmail name={name} />),
})

Configuration

Environment variables for the Resend transport:

VariableRequiredDescription
RESEND_API_KEYYes (for Resend)Resend API key, must start with re_
RESEND_FROM_EMAILYes (for Resend)Default sender email address
EMAIL_FROMNoAlternative sender email
EMAIL_FROM_NAMENoSender display name
EMAIL_TEMPLATES_BASE_URLNoBase URL used in email template links

On this page