Reference Β· SDKs

@sendaramail/react-email

Author transactional emails as React (JSX) components and render them to bulletproof, email-client-safe HTML and plaintext that are ready for 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 to render { html, text }, then pass those values to emails.send.

Install

Add the package beside the Node SDK that you send with. React is a peer dependency. Your project needs react ^18 or ^19.

install.sh
npm install @sendaramail/react-email
# also install the Node SDK to send: npm install sendara

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. 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 is a branded wrapper with a logo header, content area, and footer with an optional unsubscribe link. Props: preview, logoUrl, brandName, footerText, unsubscribeUrl, and children. logoUrl defaults to SENDARA_LOGO_URL and brandName to "Sendara".
  • BrandButton renders an amber CTA. Props: href (required), children (the label), and variant ("primary" default or "secondary").
  • sendaraTheme exposes the colors, fonts, and radius tokens behind the components so you can match the palette in your own JSX. SENDARA_LOGO_URL is the hosted logo, and SendaraTheme is the token type.

Render a component to HTML

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

  • render(element, options?) β†’ Promise<string> with the bulletproof HTML.
  • renderText(element) β†’ Promise<string> with the plaintext part.
  • renderEmail(element, options?) β†’ Promise<{ html, text }> with both parts at once (a RenderedEmail).
render.tsx
import { renderEmail } from "@sendaramail/react-email";
import { Welcome } from "./emails/Welcome";

// Both parts in one call. Best deliverability.
const { html, text } = await renderEmail(
  <Welcome name="Ada" url="https://acme.com/verify?token=abc" />,
);
elementReactElementRequired
The email component to render. Pass 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.

verification.tsx
import { renderEmail, VerificationEmail } from "@sendaramail/react-email";

const { html, text } = await renderEmail(
  <VerificationEmail
    userName="Ada"
    verificationUrl="https://app.acme.com/verify?token=…"
    code="481920"
    expiresInMinutes={15}
    brandName="Acme"
  />,
);

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. Pass an 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.tsx
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.