Documentation for agents: an index at /llms.txt, every docs page in one file at /llms-full.txt, and Markdown for any docs page by adding .md to its URL.

@effect-kit/emails

Email for Effect.

One Email service for 16 providers. Every message is checked against the provider before it is sent.

One message, sorted into any of 16 providers: Resend, Amazon SES, Postmark, SendGrid, Mailgun, SMTP · Nodemailer, Azure, MailerSend, Brevo, Mailtrap, Scaleway, ZeptoMail, Mailchimp, Lettermint, eusend, Plunk.ResendAmazon SESPostmarkSendGridMailgunSSMTP · Nodemai…AzureMMailerSendBrevoMailtrapScalewayZeptoMailMailchimpLLetterminteeusendPlunk

Before it leaves

If the provider can't carry it, it isn't sent.

An attachment through MailerSend fails with UnsupportedFeatureError. No request is made.

Email.send0001

01Providers

Change the provider, not the program.

Pick a provider. The import and the Layer change; the table shows what it supports.

import { Effect } from "effect";
import { Email } from "@effect-kit/emails";
import * as Resend from "@effect-kit/emails/resend";

const program = Effect.gen(function* () {
const email = yield* Email;
return yield* email.send(welcome);
});

program.pipe(Effect.provide(Resend.layerConfig()));
What each provider adapter can carry. Select a provider to see its Layer.
ProviderTextHTMLBoth bodiesSender nameCCBCCReply-toHeadersAttachmentsInline imagesIdempotency
yesyesyesyesyesyesyesyesyesyesyes, replays
yesyesyesyesyesyesyesyesyesyesno
yesyesyesyesyesyesyesyesyesyesno
yesyesyesyesyesyespartly, one addressyesyesyesno
yesyesyesyesyesyesyesyesyesnono
yesyesyesyesyesyesyesyesyesyesno
yesyesyesnoyesyesyesyesyesyesno
yesyesyesyesyesyespartly, one addressyesnonono
yesyesyesyesyesyespartly, one addressyesnonoyes, rejects duplicates
yesyesyesyesyesyespartly, one addressyesyesyesno
yesyesyesyesyesyesyesyesyesnono
yesyesyesyesyesyesyesyesyesyesno
yesyesyesyesyesyesyesyesyesnono
yesyesyesyesyesyesyesyesyesyesyes, replays
yesyesyesyesyesyesyesyesyesyesyes, replays
noyesnoyesnonononononono

02Playground

Run it in your browser.

Concurrency
#1
#2
#3
#4
#5
#6
outcomes

Simulated provider: message #4 is rate limited.

const outcomes = yield* Email.sendMany(
orders.map((order) => ({ message: receipt(order) })),
{ concurrency: 3 },
);
// ReadonlyArray<Result<SendReceipt, EmailError>>, in input order

Inspired by Kit Langton's Visual Effect

03Idempotency

Retry without sending twice.

Pass an idempotency key. Each provider handles the repeat its own way, and you get a typed answer.

Provider

email.send(welcome, { idempotencyKey: "welcome/ada" })

  1. 1send
  2. 2retry, same key

Resend: keys kept 24 hours

0requests
0in the inbox

04React templates

One component, two bodies.

EmailReact.render turns a React Email component into the HTML body and the plain-text body. Change the name to see both update.

import * as EmailReact from "@effect-kit/emails/react";

const Welcome = ({ name }: { name: string }) => (
  <Html>
    <Heading>Welcome, {name}</Heading>
    <Text>Your account is ready. Sign in to finish setting up your workspace.</Text>
    <Button href="https://example.com/start">Open dashboard</Button>
  </Html>
);

const content = yield* EmailReact.render(<Welcome name="Ada" />); // { html, text }
yield* email.send({ from, to, subject: "Welcome", ...content });

from hello@example.com

subject Welcome

Welcome, Ada

Your account is ready. Sign in to finish setting up your workspace.

Open dashboard

Sent with @effect-kit/emails

Get started

Send your first email from Effect.

pnpm add @effect-kit/emails effect resend