npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@cfast/email

v0.1.1

Published

Plugin-based email for Cloudflare Workers with react-email rendering

Readme

@cfast/email

Send emails from Cloudflare Workers. Write them with react-email. Swap providers with a plugin.

Cloudflare Workers can't use SMTP. Most email libraries assume Node.js. @cfast/email is a Workers-native email client that renders templates with react-email and sends them through a pluggable provider backend. Ships with Mailgun and a console provider for development.

Setup

// app/email.server.ts
import { createEmailClient } from "@cfast/email";
import { mailgun } from "@cfast/email/mailgun";
import { env } from "~/env";

export const email = createEmailClient({
  provider: mailgun(() => ({
    apiKey: env.get().MAILGUN_API_KEY,
    domain: env.get().MAILGUN_DOMAIN,
  })),
  from: () => `MyApp <noreply@${env.get().MAILGUN_DOMAIN}>`,
});

Both provider config and from use getter functions — they're called lazily at send time, which is the Workers-friendly pattern for accessing env bindings.

Sending Emails

import { email } from "~/email.server";
import { WelcomeEmail } from "~/email/templates/welcome";

await email.send({
  to: "[email protected]",
  subject: "Welcome to MyApp",
  react: <WelcomeEmail name="Daniel" />,
});

The client renders the React element to HTML and plain text via @react-email/render, then delegates delivery to the provider.

send(options)

| Field | Type | Description | |---|---|---| | to | string | Recipient address | | subject | string | Email subject | | react | ReactElement | react-email component to render | | from? | string | Override default sender |

Returns Promise<{ id: string }>.

Writing Templates

Templates are React components. Any valid ReactElement works — you can use @react-email/components for structure or plain JSX:

// email/templates/welcome.tsx
import { Html, Head, Body, Text, Link } from "@react-email/components";

type WelcomeEmailProps = {
  name: string;
};

export function WelcomeEmail({ name }: WelcomeEmailProps) {
  return (
    <Html>
      <Head />
      <Body>
        <Text>Hi {name},</Text>
        <Text>Welcome to MyApp.</Text>
        <Link href="https://myapp.com/dashboard">Go to Dashboard</Link>
      </Body>
    </Html>
  );
}

Providers

Mailgun

import { mailgun } from "@cfast/email/mailgun";

const provider = mailgun(() => ({
  apiKey: env.get().MAILGUN_API_KEY,
  domain: env.get().MAILGUN_DOMAIN,
}));

Sends via Mailgun's HTTP API using fetch and FormData. Config getter is called at send time.

Console (Development)

import { console as consoleDev } from "@cfast/email/console";

const provider = consoleDev();

Logs email details to console. Returns { id: "console-{uuid}" }. Useful for local development without sending real emails.

[cfast/email] Email sent (dev mode):
  ID: console-a1b2c3d4-...
  To: [email protected]
  From: MyApp <[email protected]>
  Subject: Welcome to MyApp
  HTML: 1234 chars

Custom Providers

A provider is a plain object matching the EmailProvider type:

import type { EmailProvider, EmailMessage } from "@cfast/email";
import { EmailDeliveryError } from "@cfast/email";

const myProvider: EmailProvider = {
  name: "my-provider",

  async send(message: EmailMessage): Promise<{ id: string }> {
    const response = await fetch("https://api.myprovider.com/send", {
      method: "POST",
      headers: { Authorization: `Bearer ${apiKey}` },
      body: JSON.stringify({
        to: message.to,
        from: message.from,
        subject: message.subject,
        html: message.html,
        text: message.text,
      }),
    });

    if (!response.ok) {
      throw new EmailDeliveryError(await response.text(), {
        provider: "my-provider",
        statusCode: response.status,
        response: await response.text(),
      });
    }

    const data = await response.json();
    return { id: data.id };
  },
};

Error Handling

Providers throw EmailDeliveryError on delivery failures:

import { EmailDeliveryError } from "@cfast/email";

try {
  await email.send({ to, subject, react: <Template /> });
} catch (error) {
  if (error instanceof EmailDeliveryError) {
    console.error(error.provider);    // "mailgun"
    console.error(error.statusCode);  // 401
    console.error(error.response);    // "Unauthorized"
  }
}

The client does not catch — callers decide whether to fire-and-forget or handle errors.

Package Exports

@cfast/email         → createEmailClient, EmailDeliveryError, types
@cfast/email/mailgun → mailgun
@cfast/email/console → console