Reference · SDKs

@sendaramail/react-email

Author transactional emails as React (JSX) components and render them to bulletproof, email-client-safe HTML and plaintext — ready to pass straight to emails.send.

@sendaramail/react-email lets you write emails the same way you write your app: as React components. It is a thin layer over @react-email/components with three additions — a curated set of re-exported building blocks, an async render family that returns both HTML and plaintext, and a few Sendara-branded components and ready-made templates. The output is inline-styled, table-based markup that survives Outlook, Gmail, and Apple Mail, so you keep JSX ergonomics without hand-tuning for every client.

This package only authors and renders email markup — it never sends. Pair it with the sendara Node SDK: render to { html, text }, then pass those to emails.send.

Install

Add the package alongside the Node SDK you'll send with. React is a peer dependencyreact ^18 or ^19 (with react-dom) must already be in your project.

Author an email in JSX

An email is just a component that returns JSX. The curated re-exports cover every primitive you need: Html, Head, Body, Container, Section, Row, Column, Text, Heading, Button, Img, Hr, Link, Preview, plus Font and Tailwind. Style with inline style objects — they get inlined into bulletproof markup at render time.

emails/Welcome.tsx
import {
  Html,
  Head,
  Body,
  Container,
  Section,
  Heading,
  Text,
  Button,
  Hr,
} from "@sendaramail/react-email";

export function Welcome({ name, url }: { name: string; url: string }) {
  return (
    <Html lang="en">
      <Head />
      <Body style={{ backgroundColor: "#fdfcfa", fontFamily: "sans-serif" }}>
        <Container style={{ maxWidth: 560, margin: "0 auto", padding: 32 }}>
          <Section>
            <Heading style={{ fontSize: 22 }}>Welcome, {name} 👋</Heading>
            <Text>Thanks for signing up. Confirm your address to get going.</Text>
            <Button
              href={url}
              style={{
                backgroundColor: "#d97706",
                borderRadius: 10,
                color: "#fff",
                padding: "12px 24px",
              }}
            >
              Confirm email
            </Button>
          </Section>
          <Hr style={{ borderColor: "#e8e3da", margin: "24px 0" }} />
          <Text style={{ color: "#6b6457", fontSize: 12 }}>Sent with Sendara.</Text>
        </Container>
      </Body>
    </Html>
  );
}
All re-exported names match React Email exactly, so any React Email guide or component works here — just import from @sendaramail/react-email instead.

Sendara-branded building blocks

Skip the boilerplate with the ink/amber-themed components. Wrap your content in EmailLayout for a logo header, padded content area, and footer; use BrandButton for the CTA. The same email as above, branded:

emails/Welcome.tsx
import {
  EmailLayout,
  BrandButton,
  Heading,
  Text,
} from "@sendaramail/react-email";

export function Welcome({ name, url }: { name: string; url: string }) {
  return (
    <EmailLayout preview={`Welcome aboard, ${name}`} unsubscribeUrl="https://acme.com/unsub">
      <Heading>Welcome, {name} 👋</Heading>
      <Text>Thanks for signing up. Confirm your address to get started.</Text>
      <BrandButton href={url}>Confirm email</BrandButton>
    </EmailLayout>
  );
}
  • EmailLayout — branded wrapper (logo header, content, footer with optional unsubscribe). Props: preview, logoUrl, brandName, footerText, unsubscribeUrl, and children. logoUrl defaults to SENDARA_LOGO_URL and brandName to "Sendara".
  • BrandButton — amber CTA. Props: href (required), children (the label), and variant ("primary" default or "secondary").
  • sendaraTheme — the colors, fonts, and radius tokens behind the components, exported so you can match the palette in your own JSX. SENDARA_LOGO_URL is the hosted logo, and SendaraTheme is the token type.

Rendering

Three async functions turn an element into deliverable markup. Each takes the instantiated component (JSX) as its first argument and returns a Promiseawait them.

  • render(element, options?) Promise<string> — the bulletproof HTML.
  • renderText(element) Promise<string> — the plaintext part.
  • renderEmail(element, options?) Promise<{ html, text }> — both at once (a RenderedEmail).
elementReactElementRequired
The email component to render — instantiated JSX, e.g. <Welcome name="Ada" />. Passing a falsy value throws a TypeError.
options.prettybooleanOptional
Beautify the HTML output (indentation + line breaks). Defaults to false for compact, production-ready markup. Accepted by render() and renderEmail(); renderText() takes no options.
Reach for renderEmail in almost every case — a multipart message with both an html and a text part lands better and renders in text-only clients. RenderOptions and RenderedEmail are exported types if you need them.

Ready-made templates

Two common transactional emails ship pre-built and branded. Render them like any component — pass props, get markup.

VerificationEmail

Email confirmation with a verify button and an optional one-time code.

verificationUrlstringRequired
Target of the verify button.
codestringOptional
Optional one-time code, shown in a prominent amber code box above the button.
userNamestringOptional
Personalizes the greeting ("Hi Ada, …") when present.
expiresInMinutesnumberOptional
Expiry shown in the fine print. Defaults to 15.
brandNamestringOptional
Brand name in the header, preview text, and copy. Defaults to "Sendara".

ReceiptEmail

An itemized purchase receipt. ReceiptLineItem is exported for typing your items array.

receiptNumberstringRequired
Receipt identifier, shown in the meta line and preview text.
datestringRequired
Human-formatted receipt date — already-formatted string, not a Date.
itemsReceiptLineItem[]Required
Line items, each { description, amount, quantity? }. amount is a pre-formatted currency string.
totalstringRequired
Pre-formatted total, emphasized in amber at the bottom.
customerNamestringOptional
Prepended to the meta line when present.
brandNamestringOptional
Brand name in the header and preview text. Defaults to "Sendara".

End-to-end: author → render → send

The full flow ties the two packages together. Author the JSX, render to { html, text } with renderEmail, then hand both parts to the Node SDK's emails.send({ from, to, subject, html, text }).

send-welcome.ts
import { Sendara } from "sendara";
import { renderEmail } from "@sendaramail/react-email";
import { Welcome } from "./emails/Welcome";

const sendara = new Sendara(process.env.SENDARA_API_KEY!);

// 1. Author JSX → 2. render to { html, text }.
const { html, text } = await renderEmail(
  <Welcome name="Ada" url="https://acme.com/verify?token=abc" />,
);

// 3. Hand both parts to the Node SDK's emails.send.
const { id } = await sendara.emails.send({
  from: "[email protected]", // must be on a verified domain
  to: "[email protected]",
  subject: "Welcome to Acme",
  html,
  text,
});

console.log(id); // msg_a1b2c3
The from address must be on a verified domain. Sends are idempotent — the SDK attaches a generated idempotency_key so retries never double-send. See the Send email guide for the rest of emails.send.
Building a campaign instead of a one-off? Render once, then reuse the { html, text } across a broadcast or batch send — rendering is pure, so you only pay for it once.