Send React Email templates through Envello
React Email's render() produces a plain HTML string. Envello's POST /emails takes an html field. That's the entire integration - no plugin, no adapter.
React Email ships components and a renderer as separate packages. You only need the renderer at send time; the component package is for building the template itself.
npm install @react-email/components @react-email/renderA React Email template is a regular React component. Write it once, preview it with React Email's own dev server, and reuse it anywhere you can call render().
// emails/welcome-email.tsx
import {
Body,
Container,
Head,
Heading,
Html,
Text,
} from "@react-email/components";
export function WelcomeEmail({ name }: { name: string }) {
return (
<Html>
<Head />
<Body style={{ fontFamily: "sans-serif", backgroundColor: "#f4f4f5" }}>
<Container style={{ padding: "24px", backgroundColor: "#ffffff" }}>
<Heading as="h2">Welcome, {name}!</Heading>
<Text>
Thanks for signing up. Your account is ready, and your first
API key is waiting in the dashboard.
</Text>
</Container>
</Body>
</Html>
);
}Call render() right before you send, and pass the result straight into the html field of POST /emails. Using plain fetch:
import { render } from "@react-email/render";
import { WelcomeEmail } from "./emails/welcome-email";
// render() returns a plain HTML string - that's the whole integration.
// Envello's POST /emails takes that string directly in the `html` field,
// no adapter or plugin needed.
const html = await render(<WelcomeEmail name="Svenja" />);
const response = await fetch("https://api.envello.dev/v1/emails", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ENVELLO_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
from: "Acme <[email protected]>",
to: "[email protected]",
subject: "Welcome to Acme",
html,
}),
});
const result = await response.json();
// { id: "b1e2...", status: "queued" }Or with the Node SDK, which wraps the same request shape:
import { Envello } from "envello";
import { render } from "@react-email/render";
import { WelcomeEmail } from "./emails/welcome-email";
const envello = new Envello(process.env.ENVELLO_API_KEY!);
await envello.emails.send({
from: "Acme <[email protected]>",
to: "[email protected]",
subject: "Welcome to Acme",
html: await render(<WelcomeEmail name="Svenja" />),
});POST /emails requires at least one of html or text. React Email's renderer can produce both from the same component, so there's rarely a reason to skip the text version.
import { render } from "@react-email/render";
import { WelcomeEmail } from "./emails/welcome-email";
const element = <WelcomeEmail name="Svenja" />;
await envello.emails.send({
from: "Acme <[email protected]>",
to: "[email protected]",
subject: "Welcome to Acme",
html: await render(element),
// React Email's plain-text renderer - cheap to add, and it's what most
// spam filters and screen readers fall back to if HTML rendering fails.
text: await render(element, { plainText: true }),
});A note on attachments and headers
React Email only produces the message body. Everything else on the request, to, cc/bcc, reply_to, headers, attachments, and send_at for scheduled sends, is set the same way whether the body came from React Email, a template string, or a templating engine. See the docs home for the full request reference.