multi-surface-render

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Multi-Surface Rendering with json-render

基于json-render的多表面渲染

Define once, render everywhere. A single json-render catalog and spec can produce React web UIs, PDF reports, HTML emails, Remotion demo videos, and OG images — each surface gets its own registry that maps catalog types to platform-native components.
一次定义,随处渲染。单个json-render目录与规范可生成React网页UI、PDF报告、HTML邮件、Remotion演示视频以及OG图片——每个渲染表面都有专属注册表,用于将目录类型映射为平台原生组件。

Quick Reference

速查参考

CategoryRulesImpactWhen to Use
Target Selection1HIGHChoosing which renderer for your use case
React Renderer1MEDIUMWeb apps, SPAs, dashboards
PDF & Email Renderer1HIGHReports, documents, notifications
Video & Image Renderer1MEDIUMDemo videos, OG images, social cards
Registry Mapping1HIGHPlatform-specific component implementations
Total: 5 rules across 5 categories
分类规则数影响级别使用场景
目标选择1为你的使用场景选择合适的渲染器
React渲染器1网页应用、单页应用、仪表盘
PDF & 邮件渲染器1报告、文档、通知
视频 & 图片渲染器1演示视频、OG图片、社交卡片
注册表映射1平台专属组件实现
总计:5个分类下共5条规则

How Multi-Surface Rendering Works

多表面渲染的工作原理

  1. One catalog — Zod-typed component definitions shared across all surfaces
  2. One spec — flat-tree JSON/YAML describing the UI structure
  3. Many registries — each surface maps catalog types to its own component implementations
  4. Many renderers — each package renders the spec using its registry
The catalog is the contract. The spec is the data. The registry is the platform-specific implementation.
  1. 一个目录 —— 所有渲染表面共享的Zod类型化组件定义
  2. 一个规范 —— 描述UI结构的扁平树状JSON/YAML
  3. 多个注册表 —— 每个渲染表面将目录类型映射为自身的组件实现
  4. 多个渲染器 —— 每个包使用对应的注册表渲染规范
目录是契约,规范是数据,注册表是平台专属实现。

Quick Start — Same Catalog, Different Renderers

快速入门 —— 同一目录,不同渲染器

Shared Catalog (used by all surfaces)

共享目录(所有表面通用)

typescript
import { defineCatalog } from '@json-render/core'
import { z } from 'zod'

export const catalog = defineCatalog({
  Heading: {
    props: z.object({
      text: z.string(),
      level: z.enum(['h1', 'h2', 'h3']),
    }),
    children: false,
  },
  Paragraph: {
    props: z.object({ text: z.string() }),
    children: false,
  },
  StatCard: {
    props: z.object({
      label: z.string(),
      value: z.string(),
      trend: z.enum(['up', 'down', 'flat']).optional(),
    }),
    children: false,
  },
})
typescript
import { defineCatalog } from '@json-render/core'
import { z } from 'zod'

export const catalog = defineCatalog({
  Heading: {
    props: z.object({
      text: z.string(),
      level: z.enum(['h1', 'h2', 'h3']),
    }),
    children: false,
  },
  Paragraph: {
    props: z.object({ text: z.string() }),
    children: false,
  },
  StatCard: {
    props: z.object({
      label: z.string(),
      value: z.string(),
      trend: z.enum(['up', 'down', 'flat']).optional(),
    }),
    children: false,
  },
})

Render to Web (React)

渲染为网页(React)

tsx
import { Renderer } from '@json-render/react'
import { catalog } from './catalog'
import { webRegistry } from './registries/web'

export const Dashboard = ({ spec }) => (
  <Renderer spec={spec} catalog={catalog} registry={webRegistry} />
)
tsx
import { Renderer } from '@json-render/react'
import { catalog } from './catalog'
import { webRegistry } from './registries/web'

export const Dashboard = ({ spec }) => (
  <Renderer spec={spec} catalog={catalog} registry={webRegistry} />
)

Render to PDF

渲染为PDF

typescript
import { renderToBuffer, renderToFile } from '@json-render/react-pdf'
import { catalog } from './catalog'
import { pdfRegistry } from './registries/pdf'

// Buffer for HTTP response
const buffer = await renderToBuffer(spec, { catalog, registry: pdfRegistry })

// Direct file output
await renderToFile(spec, './output/report.pdf', { catalog, registry: pdfRegistry })
typescript
import { renderToBuffer, renderToFile } from '@json-render/react-pdf'
import { catalog } from './catalog'
import { pdfRegistry } from './registries/pdf'

// 用于HTTP响应的Buffer
const buffer = await renderToBuffer(spec, { catalog, registry: pdfRegistry })

// 直接输出到文件
await renderToFile(spec, './output/report.pdf', { catalog, registry: pdfRegistry })

Render to Email

渲染为邮件

typescript
import { renderToHtml } from '@json-render/react-email'
import { catalog } from './catalog'
import { emailRegistry } from './registries/email'

const html = await renderToHtml(spec, { catalog, registry: emailRegistry })
await sendEmail({ to: user.email, subject: 'Weekly Report', html })
typescript
import { renderToHtml } from '@json-render/react-email'
import { catalog } from './catalog'
import { emailRegistry } from './registries/email'

const html = await renderToHtml(spec, { catalog, registry: emailRegistry })
await sendEmail({ to: user.email, subject: 'Weekly Report', html })

Render to OG Image (Satori)

渲染为OG图片(Satori)

typescript
import { renderToSvg, renderToPng } from '@json-render/image'
import { catalog } from './catalog'
import { imageRegistry } from './registries/image'

const png = await renderToPng(spec, {
  catalog,
  registry: imageRegistry,
  width: 1200,
  height: 630,
})
typescript
import { renderToSvg, renderToPng } from '@json-render/image'
import { catalog } from './catalog'
import { imageRegistry } from './registries/image'

const png = await renderToPng(spec, {
  catalog,
  registry: imageRegistry,
  width: 1200,
  height: 630,
})

Render to Video (Remotion)

渲染为视频(Remotion)

tsx
import { JsonRenderComposition } from '@json-render/remotion'
import { catalog } from './catalog'
import { remotionRegistry } from './registries/remotion'

export const DemoVideo = () => (
  <JsonRenderComposition
    spec={spec}
    catalog={catalog}
    registry={remotionRegistry}
    fps={30}
    durationInFrames={150}
  />
)
tsx
import { JsonRenderComposition } from '@json-render/remotion'
import { catalog } from './catalog'
import { remotionRegistry } from './registries/remotion'

export const DemoVideo = () => (
  <JsonRenderComposition
    spec={spec}
    catalog={catalog}
    registry={remotionRegistry}
    fps={30}
    durationInFrames={150}
  />
)

Decision Matrix — When to Use Each Target

决策矩阵 —— 何时使用各渲染目标

TargetPackageWhen to UseOutput
React
@json-render/react
Web apps, SPAsJSX
Vue
@json-render/vue
Vue projectsVue components
Svelte
@json-render/svelte
Svelte projectsSvelte components
React Native
@json-render/react-native
Mobile apps (25+ components)Native views
PDF
@json-render/react-pdf
Reports, documentsPDF buffer/file
Email
@json-render/react-email
Notifications, digestsHTML string
Remotion
@json-render/remotion
Demo videos, marketingMP4/WebM
Image
@json-render/image
OG images, social cardsSVG/PNG (Satori)
YAML
@json-render/yaml
Token optimizationYAML string
MCP
@json-render/mcp
Claude/Cursor conversationsSandboxed iframe
3D
@json-render/react-three-fiber
3D scenes (19 components)Three.js canvas
Codegen
@json-render/codegen
Source code from specsTypeScript/JSX
Load
rules/target-selection.md
for detailed selection criteria and trade-offs.
目标使用场景输出格式
React
@json-render/react
网页应用、单页应用JSX
Vue
@json-render/vue
Vue项目Vue组件
Svelte
@json-render/svelte
Svelte项目Svelte组件
React Native
@json-render/react-native
移动应用(25+组件)原生视图
PDF
@json-render/react-pdf
报告、文档PDF Buffer/文件
Email
@json-render/react-email
通知、摘要HTML字符串
Remotion
@json-render/remotion
演示视频、营销视频MP4/WebM
Image
@json-render/image
OG图片、社交卡片SVG/PNG(基于Satori)
YAML
@json-render/yaml
令牌优化YAML字符串
MCP
@json-render/mcp
Claude/Cursor对话沙箱化iframe
3D
@json-render/react-three-fiber
3D场景(19个组件)Three.js画布
代码生成
@json-render/codegen
从规范生成源代码TypeScript/JSX
加载
rules/target-selection.md
查看详细的选择标准与权衡。

PDF Renderer — Reports and Documents

PDF渲染器 —— 报告与文档

The
@json-render/react-pdf
package renders specs to PDF using react-pdf under the hood. Three output modes: buffer, file, and stream.
typescript
import { renderToBuffer, renderToFile, renderToStream } from '@json-render/react-pdf'

// In-memory buffer (for HTTP responses, S3 upload)
const buffer = await renderToBuffer(spec, { catalog, registry: pdfRegistry })
res.setHeader('Content-Type', 'application/pdf')
res.send(buffer)

// Direct file write
await renderToFile(spec, './output/report.pdf', { catalog, registry: pdfRegistry })

// Streaming (for large documents)
const stream = await renderToStream(spec, { catalog, registry: pdfRegistry })
stream.pipe(res)
Load
rules/pdf-email-renderer.md
for PDF registry patterns and email rendering.
@json-render/react-pdf
包基于react-pdf将规范渲染为PDF。支持三种输出模式:Buffer、文件与流。
typescript
import { renderToBuffer, renderToFile, renderToStream } from '@json-render/react-pdf'

// 内存中的Buffer(用于HTTP响应、S3上传)
const buffer = await renderToBuffer(spec, { catalog, registry: pdfRegistry })
res.setHeader('Content-Type', 'application/pdf')
res.send(buffer)

// 直接写入文件
await renderToFile(spec, './output/report.pdf', { catalog, registry: pdfRegistry })

// 流式输出(适用于大型文档)
const stream = await renderToStream(spec, { catalog, registry: pdfRegistry })
stream.pipe(res)
加载
rules/pdf-email-renderer.md
查看PDF注册表模式与邮件渲染内容。

Image Renderer — OG Images and Social Cards

图片渲染器 —— OG图片与社交卡片

The
@json-render/image
package uses Satori to convert specs to SVG, then optionally to PNG. Designed for server-side generation of social media images.
typescript
import { renderToSvg, renderToPng } from '@json-render/image'

// SVG output (smaller, scalable)
const svg = await renderToSvg(spec, {
  catalog,
  registry: imageRegistry,
  width: 1200,
  height: 630,
})

// PNG output (universal compatibility)
const png = await renderToPng(spec, {
  catalog,
  registry: imageRegistry,
  width: 1200,
  height: 630,
})
Load
rules/video-image-renderer.md
for Satori constraints and Remotion composition patterns.
@json-render/image
包使用Satori将规范转换为SVG,也可选择转换为PNG。专为服务端生成社交媒体图片设计。
typescript
import { renderToSvg, renderToPng } from '@json-render/image'

// SVG输出(体积更小、可缩放)
const svg = await renderToSvg(spec, {
  catalog,
  registry: imageRegistry,
  width: 1200,
  height: 630,
})

// PNG输出(兼容性强)
const png = await renderToPng(spec, {
  catalog,
  registry: imageRegistry,
  width: 1200,
  height: 630,
})
加载
rules/video-image-renderer.md
查看Satori的限制与Remotion合成模式。

Registry Mapping — Same Catalog, Platform-Specific Components

注册表映射 —— 同一目录,平台专属组件

Each surface needs its own registry. The registry maps catalog types to platform-specific component implementations while the catalog and spec stay identical.
typescript
// Web registry — uses HTML elements
const webRegistry = {
  Heading: ({ text, level }) => {
    const Tag = level // h1, h2, h3
    return <Tag className="font-bold">{text}</Tag>
  },
  StatCard: ({ label, value, trend }) => (
    <div className="rounded border p-4">
      <span className="text-sm text-gray-500">{label}</span>
      <strong className="text-2xl">{value}</strong>
    </div>
  ),
}

// PDF registry — uses react-pdf primitives
import { Text, View } from '@react-pdf/renderer'
const pdfRegistry = {
  Heading: ({ text, level }) => (
    <Text style={{ fontSize: level === 'h1' ? 24 : level === 'h2' ? 18 : 14 }}>
      {text}
    </Text>
  ),
  StatCard: ({ label, value }) => (
    <View style={{ border: '1pt solid #ccc', padding: 8 }}>
      <Text style={{ fontSize: 10, color: '#666' }}>{label}</Text>
      <Text style={{ fontSize: 18, fontWeight: 'bold' }}>{value}</Text>
    </View>
  ),
}
Load
rules/registry-mapping.md
for registry creation patterns and type safety.
每个渲染表面需要专属注册表。注册表将目录类型映射为平台专属组件实现,而目录与规范保持一致。
typescript
// 网页注册表 —— 使用HTML元素
const webRegistry = {
  Heading: ({ text, level }) => {
    const Tag = level // h1, h2, h3
    return <Tag className="font-bold">{text}</Tag>
  },
  StatCard: ({ label, value, trend }) => (
    <div className="rounded border p-4">
      <span className="text-sm text-gray-500">{label}</span>
      <strong className="text-2xl">{value}</strong>
    </div>
  ),
}

// PDF注册表 —— 使用react-pdf原语
import { Text, View } from '@react-pdf/renderer'
const pdfRegistry = {
  Heading: ({ text, level }) => (
    <Text style={{ fontSize: level === 'h1' ? 24 : level === 'h2' ? 18 : 14 }}>
      {text}
    </Text>
  ),
  StatCard: ({ label, value }) => (
    <View style={{ border: '1pt solid #ccc', padding: 8 }}>
      <Text style={{ fontSize: 10, color: '#666' }}>{label}</Text>
      <Text style={{ fontSize: 18, fontWeight: 'bold' }}>{value}</Text>
    </View>
  ),
}
加载
rules/registry-mapping.md
查看注册表创建模式与类型安全。

Rule Details

规则详情

Target Selection

目标选择

Decision criteria for choosing the right renderer target.
RuleFileKey Pattern
Target Selection
rules/target-selection.md
Use case mapping, output format constraints
选择合适渲染目标的决策标准。
规则文件核心模式
目标选择
rules/target-selection.md
用例映射、输出格式约束

React Renderer

React渲染器

Web rendering with the
<Renderer>
component.
RuleFileKey Pattern
React Renderer
rules/react-renderer.md
<Renderer>
component, streaming, error boundaries
使用
<Renderer>
组件进行网页渲染。
规则文件核心模式
React渲染器
rules/react-renderer.md
<Renderer>
组件、流式渲染、错误边界

PDF & Email Renderer

PDF & 邮件渲染器

Server-side rendering to PDF buffers/files and HTML email strings.
RuleFileKey Pattern
PDF & Email
rules/pdf-email-renderer.md
renderToBuffer, renderToFile, renderToHtml
服务端渲染为PDF Buffer/文件与HTML邮件字符串。
规则文件核心模式
PDF & 邮件
rules/pdf-email-renderer.md
renderToBuffer、renderToFile、renderToHtml

Video & Image Renderer

视频 & 图片渲染器

Remotion compositions and Satori image generation.
RuleFileKey Pattern
Video & Image
rules/video-image-renderer.md
JsonRenderComposition, renderToPng, renderToSvg
Remotion合成与Satori图片生成。
规则文件核心模式
视频 & 图片
rules/video-image-renderer.md
JsonRenderComposition、renderToPng、renderToSvg

Registry Mapping

注册表映射

Creating platform-specific registries for a shared catalog.
RuleFileKey Pattern
Registry Mapping
rules/registry-mapping.md
Per-platform registries, type-safe mapping
为共享目录创建平台专属注册表。
规则文件核心模式
注册表映射
rules/registry-mapping.md
按平台划分的注册表、类型安全映射

Key Decisions

关键决策

DecisionRecommendation
PDF libraryUse
@json-render/react-pdf
(react-pdf), not Puppeteer screenshots
Email renderingUse
@json-render/react-email
(react-email), not MJML or custom HTML
OG imagesUse
@json-render/image
(Satori), not Puppeteer or canvas
VideoUse
@json-render/remotion
(Remotion), not FFmpeg scripts
Registry per platformAlways separate registries; never one registry for all surfaces
Catalog sharingOne catalog definition shared via import across all registries
决策推荐方案
PDF库使用
@json-render/react-pdf
(基于react-pdf),而非Puppeteer截图
邮件渲染使用
@json-render/react-email
(基于react-email),而非MJML或自定义HTML
OG图片使用
@json-render/image
(基于Satori),而非Puppeteer或canvas
视频使用
@json-render/remotion
(基于Remotion),而非FFmpeg脚本
平台注册表始终分离注册表;绝不为所有表面使用单个注册表
目录共享单个目录定义通过导入在所有注册表中共享

Common Mistakes

常见错误

  1. Building separate component trees for each surface — defeats the purpose; share the catalog and spec
  2. Using Puppeteer to screenshot React for PDF generation — slow, fragile; use native react-pdf rendering
  3. One giant registry covering all platforms — impossible since PDF uses
    <View>
    /
    <Text>
    , web uses
    <div>
    /
    <span>
  4. Forgetting Satori limitations — no CSS grid, limited flexbox; design image registries with these constraints
  5. Duplicating catalog definitions per surface — one catalog, many registries; the catalog is the contract
  1. 为每个渲染表面构建独立组件树——违背初衷;应共享目录与规范
  2. 使用Puppeteer截图React生成PDF——速度慢、易出错;使用原生react-pdf渲染
  3. 单个巨型注册表覆盖所有平台——不可能实现,因为PDF使用
    <View>
    /
    <Text>
    ,网页使用
    <div>
    /
    <span>
  4. 忽略Satori的限制——不支持CSS Grid,Flexbox功能有限;设计图片注册表时需考虑这些约束
  5. 为每个渲染表面重复定义目录——一个目录,多个注册表;目录是契约

Related Skills

相关技能

  • ork:json-render-catalog
    — Catalog definition patterns with Zod, shadcn components
  • ork:demo-producer
    — Video production pipeline using Remotion
  • ork:presentation-builder
    — Slide deck generation
  • ork:mcp-visual-output
    — Rendering specs in Claude/Cursor via MCP
  • ork:json-render-catalog
    —— 结合Zod、shadcn组件的目录定义模式
  • ork:demo-producer
    —— 使用Remotion的视频制作流水线
  • ork:presentation-builder
    —— 幻灯片生成
  • ork:mcp-visual-output
    —— 通过MCP在Claude/Cursor中渲染规范