
React Email
Render JSON email specs to HTML or plain text with @json-render/react-email and @react-email/components for transactional and marketing templates in code-first solo products.
Overview
React-email is an agent skill for the Build phase that converts JSON email specs into HTML or plain-text output using @json-render/react-email and React Email components.
Install
npx skills add https://github.com/vercel-labs/json-render --skill react-emailWhat is this skill?
- Renders JSON specs to HTML or plain text via renderToHtml and @json-render/react-email
- Uses defineCatalog with standardComponentDefinitions on @json-render/core schema
- Covers transactional and marketing emails, email catalogs, and AI-generated email specs
- Quick-start TypeScript example with Html, Head, Body, Container, Heading elements
- Tagged for json-render, HTML email, and transactional email workflows
Adoption & trust: 740 installs on skills.sh; 15k GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
You have JSON or AI-generated email structure but no consistent pipeline to render valid HTML and text with React Email primitives.
Who is it for?
Developers already using json-render who want programmatic email templates shared with agent-generated specs.
Skip if: Teams that only need one-off copy in a no-code ESP with no JSON catalog or React codebase.
When should I use this skill?
Working with @json-render/react-email, JSON transactional or marketing emails, email catalogs, AI-generated email specs, react-email, HTML email, or transactional email.
What do I get? / Deliverables
You can wire a catalog-backed renderToHtml flow and reusable spec elements for transactional or marketing sends inside your app or agent toolchain.
- HTML and/or plain-text email output from a JSON spec
- Catalog wiring with standardComponentDefinitions
Recommended Skills
Journey fit
Email rendering from structured specs is implemented during product build as frontend and integration work, not a separate launch-only task. React Email component catalogs and render pipelines live alongside other UI and content delivery code.
How it compares
Code-first JSON renderer for React Email—not an MCP server and not a generic markdown-to-email converter.
Common Questions / FAQ
Who is react-email for?
Solo builders and indie devs shipping SaaS or agent products who render emails from JSON specs with Vercel json-render and React Email.
When should I use react-email?
Use during build when implementing transactional email, marketing templates from JSON, email catalogs, or AI-generated email specs—when users mention react-email, HTML email, or @json-render/react-email.
Is react-email safe to install?
Treat it like any npm-adjacent skill package: review the Security Audits panel on this Prism page and your dependency lockfile before enabling network installs in agents.
SKILL.md
READMESKILL.md - React Email
# @json-render/react-email React Email renderer that converts JSON specs into HTML or plain-text email output. ## Quick Start ```typescript import { renderToHtml } from "@json-render/react-email"; import { schema, standardComponentDefinitions } from "@json-render/react-email"; import { defineCatalog } from "@json-render/core"; const catalog = defineCatalog(schema, { components: standardComponentDefinitions, }); const 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); ``` ## Spec Structure (Element Tree) Same flat element tree as `@json-render/react`: `root` key plus `elements` map. Root must be `Html`; children of `Html` should be `Head` and `Body`. Use `Container` (e.g. max-width 600px) inside `Body` for client-safe layout. ## Creating a Catalog and Registry ```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 }); ``` ## Server-Side Render APIs | Function | Purpose | |----------|---------| | `renderToHtml(spec, options?)` | Render spec to HTML email string | | `renderToPlainText(spec, options?)` | Render spec to plain-text email string | `RenderOptions`: `registry`, `includeStandard` (default true), `state` (for `$state` / `$cond`). ## Visibility and State Supports `visible` conditions, `$state`, `$cond`, repeat (`repeat.statePath`), and the same expression syntax as `@json-render/react`. Use `state` in `RenderOptions` when rendering server-side so expressions resolve. ## Server-Safe Import Import schema and catalog without React or `@react-email/components`: ```typescript import { schema, standardComponentDefinitions } from "@json-render/react-email/server"; ``` ## Key Exports | Export | Purpose | |--------|---------| | `defineRegistry` | Create type-safe component registry from catalog | | `Renderer` | Render spec in browser (e.g. preview); use with `JSONUIProvider` for state/actions | | `createRenderer` | Standalone renderer component with