Files
b6f12d4d53 Feat/react email support (#159)
* add react-email workspace dependencies

* add react-email package

* add react-email example with Vercel Invite, Stripe Welcome and Nike Receipt

* add render tests for react-email package

* fix missing style prop on stripe-welcome logo Image

* add react email documentation

* add json render react email skill

* fix @internal/react-state alias in vitest config

Pre-existing issue: vitest could not resolve @internal/react-state,
causing 6 test suites in packages/react and packages/react-pdf to fail.

* fix PR review feedback

- fix tsconfig: remove rootDir, expand include for test imports
- fix render tests: add non-null assertions for noUncheckedIndexedAccess
- fix dev script: add portless to match other examples
- fix .env.example: correct comment from video to email generation
- fix docs: add blank line before heading in installation page

* merge latest

* fix: update tsconfig extends from @repo to @internal/typescript-config

* fixes

* fixes

---------

Co-authored-by: WManzoli <willmanzoli@gmail.com>
Co-authored-by: Chris Tate <chris@ctate.dev>
2026-03-02 16:43:03 -06:00

148 lines
4.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# @json-render/react-email
React Email renderer for `@json-render/core`. Generate HTML and plain-text emails from JSON specs using `@react-email/components` and `@react-email/render`.
## Install
```bash
npm install @json-render/core @json-render/react-email @react-email/components @react-email/render
```
## Quick Start
### Render a spec to HTML
```typescript
import { renderToHtml } from "@json-render/react-email";
import type { Spec } from "@json-render/core";
const spec: Spec = {
root: "html-1",
elements: {
"html-1": { type: "Html", props: { lang: "en", dir: "ltr" }, children: ["head-1", "body-1"] },
"head-1": { type: "Head", props: {}, children: [] },
"body-1": {
type: "Body",
props: { style: { backgroundColor: "#f6f9fc" } },
children: ["container-1"],
},
"container-1": {
type: "Container",
props: { style: { maxWidth: "600px", margin: "0 auto", padding: "20px" } },
children: ["heading-1", "text-1"],
},
"heading-1": { type: "Heading", props: { text: "Welcome" }, children: [] },
"text-1": { type: "Text", props: { text: "Thanks for signing up." }, children: [] },
},
};
const html = await renderToHtml(spec);
```
### With a custom catalog
```typescript
import { defineCatalog } from "@json-render/core";
import { schema, defineRegistry, renderToHtml } from "@json-render/react-email";
import { standardComponentDefinitions } from "@json-render/react-email/catalog";
import { Container, Heading, Text } from "@react-email/components";
import { z } from "zod";
const catalog = defineCatalog(schema, {
components: {
...standardComponentDefinitions,
Alert: {
props: z.object({
message: z.string(),
variant: z.enum(["info", "success", "warning"]).nullable(),
}),
slots: [],
description: "A highlighted message block",
},
},
actions: {},
});
const { registry } = defineRegistry(catalog, {
components: {
Alert: ({ props }) => (
<Container style={{ padding: 16, backgroundColor: "#eff6ff", borderRadius: 8 }}>
<Text style={{ margin: 0 }}>{props.message}</Text>
</Container>
),
},
});
const html = await renderToHtml(spec, { registry });
```
## Standard Components
### Document structure
| Component | Description |
|-----------|-------------|
| `Html` | Top-level email wrapper. Must be the root element. |
| `Head` | Email head section. |
| `Body` | Email body wrapper. |
### Layout
| Component | Description |
|-----------|-------------|
| `Container` | Constrains content width. |
| `Section` | Groups related content. |
| `Row` | Horizontal layout row. |
| `Column` | Column within a Row. |
### Content
| Component | Description |
|-----------|-------------|
| `Heading` | Heading text (h1–h6). |
| `Text` | Body text paragraph. |
| `Link` | Hyperlink. |
| `Button` | Call-to-action button. |
| `Image` | Image from URL. |
| `Hr` | Horizontal rule. |
### Utility
| Component | Description |
|-----------|-------------|
| `Preview` | Inbox preview text. |
| `Markdown` | Markdown content as email-safe HTML. |
## Server-Side APIs
```typescript
import { renderToHtml, renderToPlainText } from "@json-render/react-email";
const html = await renderToHtml(spec);
const plainText = await renderToPlainText(spec);
```
Both accept an optional second argument with:
- `registry` — Custom component registry (merged with standard components)
- `includeStandard` — Include built-in standard components (default: `true`)
- `state` — Initial state for `$state` / `$cond` dynamic prop resolution
## Server-Safe Import
Import schema and catalog definitions without pulling in React or `@react-email/components`:
```typescript
import { schema, standardComponentDefinitions } from "@json-render/react-email/server";
```
## Documentation
Full API reference: [json-render.dev/docs/api/react-email](https://json-render.dev/docs/api/react-email).
Example app: [examples/react-email](https://github.com/vercel-labs/json-render/tree/main/examples/react-email).
## License
Apache-2.0