vercel-labs/json-render
GitHub: vercel-labs/json-render
一个跨平台 Generative UI 框架,让 AI 在开发者预设的组件约束下,根据自然语言提示安全、可预测地生成并渲染动态界面。
Stars: 15222 | Forks: 819
# json 渲染
**Generative UI 框架。**
从提示词生成动态、个性化的 UI,且不牺牲可靠性。提供预定义的组件和操作,确保输出安全、可预测。
```
# 用于 React
npm install @json-render/core @json-render/react
# 用于 React(带有预构建的 shadcn/ui 组件)
npm install @json-render/shadcn
# 或用于 React Native
npm install @json-render/core @json-render/react-native
# 或用于视频
npm install @json-render/core @json-render/remotion
# 或用于 PDF 文档
npm install @json-render/core @json-render/react-pdf
# 或用于 HTML 电子邮件
npm install @json-render/core @json-render/react-email @react-email/components @react-email/render
# 或用于 Vue
npm install @json-render/core @json-render/vue
# 或用于 Svelte
npm install @json-render/core @json-render/svelte
# 或用于 SolidJS
npm install @json-render/core @json-render/solid
# 或用于终端 UI
npm install @json-render/core @json-render/ink ink react
# 或用于完整的 Next.js 应用(路由、布局、SSR、metadata)
npm install @json-render/core @json-render/react @json-render/next
# 或用于 3D 场景(以及通过 GaussianSplat 组件进行 gaussian splatting)
npm install @json-render/core @json-render/react-three-fiber @react-three/fiber @react-three/drei three
```
## 为什么选择 json-render?
json-render 是一个 **Generative UI** 框架:AI 根据自然语言提示词生成界面,并受限于你定义的组件。你设定护栏(约束),AI 在其中生成:
- **受护栏保护** - AI 只能使用你目录中的组件
- **可预测** - 每次输出的 JSON 都符合你的 schema
- **快速** - 随着模型的响应进行渐进式流式传输和渲染
- **跨平台** - 同一个目录可支持 React、Vue、Svelte、Solid(Web)和 React Native(移动端)
- **开箱即用** - 包含 36 个预构建的 shadcn/ui 组件,可直接使用
## 快速开始
### 1. 定义你的目录
```
import { defineCatalog } from "@json-render/core";
import { schema } from "@json-render/react/schema";
import { z } from "zod";
const catalog = defineCatalog(schema, {
components: {
Card: {
props: z.object({ title: z.string() }),
description: "A card container",
},
Metric: {
props: z.object({
label: z.string(),
value: z.string(),
format: z.enum(["currency", "percent", "number"]).nullable(),
}),
description: "Display a metric value",
},
Button: {
props: z.object({
label: z.string(),
action: z.string(),
}),
description: "Clickable button",
},
},
actions: {
export_report: { description: "Export dashboard to PDF" },
refresh_data: { description: "Refresh all metrics" },
},
});
```
### 2. 定义你的组件
```
import { defineRegistry, Renderer } from "@json-render/react";
const { registry } = defineRegistry(catalog, {
components: {
Card: ({ props, children }) => (
),
Metric: ({ props }) => (
;
}
```
**就这么简单。** AI 生成 JSON,你负责安全地渲染它。
## 包
| 包 | 描述 |
| --------------------------- | ---------------------------------------------------------------------- |
| `@json-render/core` | Schemas、目录、AI prompts、动态 props、SpecStream 工具 |
| `@json-render/react` | React 渲染器、contexts、hooks |
| `@json-render/vue` | Vue 3 渲染器、composables、providers |
| `@json-render/svelte` | 基于 runes 响应式的 Svelte 5 渲染器 |
| `@json-render/solid` | 具有细粒度响应式 contexts 的 SolidJS 渲染器 |
| `@json-render/shadcn` | 36 个预构建的 shadcn/ui 组件 (Radix UI + Tailwind CSS) |
| `@json-render/shadcn-svelte`| 36 个预构建的 shadcn-svelte 组件 (Svelte 5 + Tailwind CSS) |
| `@json-render/react-three-fiber` | 用于 3D 场景的 React Three Fiber 渲染器(包含 GaussianSplat 在内的 20 个内置组件) |
| `@json-render/react-native` | 包含标准移动端组件的 React Native 渲染器 |
| `@json-render/next` | Next.js 渲染器 — JSON 结合路由、layouts、SSR 变为完整应用 |
| `@json-render/remotion` | Remotion 视频渲染器,timeline schema |
| `@json-render/react-pdf` | 根据规格说明生成 PDF 文档的 React PDF 渲染器 |
| `@json-render/react-email` | 根据规格说明生成 HTML/纯文本电子邮件的 React Email 渲染器 |
| `@json-render/ink` | 包含用于交互式 TUI 的内置组件的 Ink 终端渲染器。 |
| `@json-render/image` | 通过 Satori 输出 SVG/PNG 的图像渲染器(OG 图像,社交卡片) |
| `@json-render/directives` | 预构建的自定义指令 — $format, $math, $concat, $count, $truncate, $pluralize, $join, $t (i18n) |
| `@json-render/codegen` | 用于从 json-render UI 树生成代码的工具 |
| `@json-render/devtools` | 框架无关的 devtools 核心 — 面板 UI、事件存储、选择器、流拦截 |
| `@json-render/devtools-react` | `@json-render/devtools` 的 React 适配器(直接插入 ` `) |
| `@json-render/devtools-vue` | `@json-render/devtools` 的 Vue 适配器 |
| `@json-render/devtools-svelte` | `@json-render/devtools` 的 Svelte 适配器 |
| `@json-render/devtools-solid` | `@json-render/devtools` 的 SolidJS 适配器 |
| `@json-render/redux` | 用于 `StateStore` 的 Redux / Redux Toolkit 适配器 |
| `@json-render/zustand` | 用于 `StateStore` 的 Zustand 适配器 |
| `@json-render/jotai` | 用于 `StateStore` 的 Jotai 适配器 |
| `@json-render/xstate` | 用于 `StateStore` 的 XState Store (atom) 适配器 |
| `@json-render/mcp` | 用于 Claude、ChatGPT、Cursor、VS Code 的 MCP Apps 集成 |
| `@json-render/yaml` | 包含流式解析器、编辑模式、AI SDK 转换的 YAML 传输格式 |
## 渲染器
### React (UI)
```
import { defineRegistry, Renderer } from "@json-render/react";
import { schema } from "@json-render/react/schema";
// Flat spec format (root key + elements map)
const spec = {
root: "card-1",
elements: {
"card-1": {
type: "Card",
props: { title: "Hello" },
children: ["button-1"],
},
"button-1": {
type: "Button",
props: { label: "Click me" },
children: [],
},
},
};
// defineRegistry creates a type-safe component registry
const { registry } = defineRegistry(catalog, { components });
;
```
### Vue (UI)
```
import { h } from "vue";
import { defineRegistry, Renderer } from "@json-render/vue";
import { schema } from "@json-render/vue/schema";
const { registry } = defineRegistry(catalog, {
components: {
Card: ({ props, children }) =>
h("div", { class: "card" }, [h("h3", null, props.title), children]),
Button: ({ props, emit }) =>
h("button", { onClick: () => emit("press") }, props.label),
},
});
// In your Vue component template:
//
```
### Svelte (UI)
```
import { defineRegistry, Renderer } from "@json-render/svelte";
import { schema } from "@json-render/svelte/schema";
const { registry } = defineRegistry(catalog, {
components: {
Card: ({ props, children }) => /* Svelte 5 snippet */,
Button: ({ props, emit }) => /* Svelte 5 snippet */,
},
});
// In your Svelte component:
//
```
### Solid (UI)
```
import { defineRegistry, Renderer } from "@json-render/solid";
import { schema } from "@json-render/solid/schema";
const { registry } = defineRegistry(catalog, {
components: {
Card: (renderProps) => ;
```
### shadcn/ui (Web)
```
import { defineCatalog } from "@json-render/core";
import { schema } from "@json-render/react/schema";
import { defineRegistry, Renderer } from "@json-render/react";
import { shadcnComponentDefinitions } from "@json-render/shadcn/catalog";
import { shadcnComponents } from "@json-render/shadcn";
// Pick components from the 36 standard definitions
const catalog = defineCatalog(schema, {
components: {
Card: shadcnComponentDefinitions.Card,
Stack: shadcnComponentDefinitions.Stack,
Heading: shadcnComponentDefinitions.Heading,
Button: shadcnComponentDefinitions.Button,
},
actions: {},
});
// Use matching implementations
const { registry } = defineRegistry(catalog, {
components: {
Card: shadcnComponents.Card,
Stack: shadcnComponents.Stack,
Heading: shadcnComponents.Heading,
Button: shadcnComponents.Button,
},
});
;
```
### React Native (移动端)
```
import { defineCatalog } from "@json-render/core";
import { schema } from "@json-render/react-native/schema";
import {
standardComponentDefinitions,
standardActionDefinitions,
} from "@json-render/react-native/catalog";
import { defineRegistry, Renderer } from "@json-render/react-native";
// 25+ standard components included
const catalog = defineCatalog(schema, {
components: { ...standardComponentDefinitions },
actions: standardActionDefinitions,
});
const { registry } = defineRegistry(catalog, { components: {} });
;
```
### Remotion (视频)
```
import { Player } from "@remotion/player";
import {
Renderer,
schema,
standardComponentDefinitions,
} from "@json-render/remotion";
// Timeline spec format
const spec = {
composition: {
id: "video",
fps: 30,
width: 1920,
height: 1080,
durationInFrames: 300,
},
tracks: [{ id: "main", name: "Main", type: "video", enabled: true }],
clips: [
{
id: "clip-1",
trackId: "main",
component: "TitleCard",
props: { title: "Hello" },
from: 0,
durationInFrames: 90,
},
],
audio: { tracks: [] },
};
;
```
### React PDF (文档)
```
import { renderToBuffer } from "@json-render/react-pdf";
const spec = {
root: "doc",
elements: {
doc: {
type: "Document",
props: { title: "Invoice" },
children: ["page-1"],
},
"page-1": {
type: "Page",
props: { size: "A4" },
children: ["heading-1", "table-1"],
},
"heading-1": {
type: "Heading",
props: { text: "Invoice #1234", level: "h1" },
children: [],
},
"table-1": {
type: "Table",
props: {
columns: [
{ header: "Item", width: "60%" },
{ header: "Price", width: "40%", align: "right" },
],
rows: [
["Widget A", "$10.00"],
["Widget B", "$25.00"],
],
},
children: [],
},
},
};
// Render to buffer, stream, or file
const buffer = await renderToBuffer(spec);
```
### React Email (电子邮件)
```
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);
```
### 图像 (SVG/PNG)
```
import { renderToPng } from "@json-render/image/render";
const spec = {
root: "frame",
elements: {
frame: {
type: "Frame",
props: { width: 1200, height: 630, backgroundColor: "#1a1a2e" },
children: ["heading"],
},
heading: {
type: "Heading",
props: { text: "Hello World", level: "h1", color: "#ffffff" },
children: [],
},
},
};
// Render to PNG (requires @resvg/resvg-js)
const png = await renderToPng(spec, { fonts });
// Or render to SVG string
import { renderToSvg } from "@json-render/image/render";
const svg = await renderToSvg(spec, { fonts });
```
### Three.js (3D)
```
import { defineCatalog } from "@json-render/core";
import { schema, defineRegistry } from "@json-render/react";
import {
threeComponentDefinitions,
threeComponents,
ThreeCanvas,
} from "@json-render/react-three-fiber";
const catalog = defineCatalog(schema, {
components: {
Box: threeComponentDefinitions.Box,
Sphere: threeComponentDefinitions.Sphere,
AmbientLight: threeComponentDefinitions.AmbientLight,
DirectionalLight: threeComponentDefinitions.DirectionalLight,
GaussianSplat: threeComponentDefinitions.GaussianSplat,
OrbitControls: threeComponentDefinitions.OrbitControls,
},
actions: {},
});
const { registry } = defineRegistry(catalog, {
components: {
Box: threeComponents.Box,
Sphere: threeComponents.Sphere,
AmbientLight: threeComponents.AmbientLight,
DirectionalLight: threeComponents.DirectionalLight,
GaussianSplat: threeComponents.GaussianSplat,
OrbitControls: threeComponents.OrbitControls,
},
});
;
```
### Next.js (完整应用)
```
import type { NextAppSpec } from "@json-render/next";
import { createNextApp } from "@json-render/next/server";
import { NextAppProvider } from "@json-render/next";
const spec: NextAppSpec = {
metadata: { title: { default: "My App", template: "%s | My App" } },
layouts: {
main: {
root: "shell",
elements: {
shell: { type: "Container", props: {}, children: ["nav", "slot"] },
nav: { type: "NavBar", props: {}, children: [] },
slot: { type: "Slot", props: {}, children: [] },
},
},
},
routes: {
"/": {
layout: "main",
metadata: { title: "Home" },
page: {
root: "hero",
elements: {
hero: { type: "Card", props: { title: "Welcome" }, children: [] },
},
},
},
},
};
// Server: creates Page, generateMetadata, generateStaticParams
const app = createNextApp({ spec });
// Client: wrap your layout with NextAppProvider
//
// {children}
//
```
### shadcn-svelte (Svelte)
```
import { defineCatalog } from "@json-render/core";
import { schema } from "@json-render/svelte/schema";
import { defineRegistry, Renderer } from "@json-render/svelte";
import { shadcnComponentDefinitions } from "@json-render/shadcn-svelte/catalog";
import { shadcnComponents } from "@json-render/shadcn-svelte";
const catalog = defineCatalog(schema, {
components: {
Card: shadcnComponentDefinitions.Card,
Stack: shadcnComponentDefinitions.Stack,
Heading: shadcnComponentDefinitions.Heading,
Button: shadcnComponentDefinitions.Button,
},
actions: {},
});
const { registry } = defineRegistry(catalog, {
components: {
Card: shadcnComponents.Card,
Stack: shadcnComponents.Stack,
Heading: shadcnComponents.Heading,
Button: shadcnComponents.Button,
},
});
// In your Svelte component:
//
```
### 开发者工具
适用于任何 json-render 应用的即插即用检查器面板。包含 Spec 树、状态编辑器、操作日志、流日志、目录浏览器和 DOM 选择器。
```
// React
import { JsonRenderDevtools } from "@json-render/devtools-react";
;
```
浮动开关出现在右下角。快捷键:`Ctrl`/`Cmd` + `Shift` + `J`。在生产环境中会通过 tree-shaking 裁剪为 `null`。
适用于 React、Vue、Svelte 和 Solid — 将 `@json-render/devtools-react` 替换为与你的渲染器匹配的适配器。
### Ink (终端)
```
import { defineCatalog } from "@json-render/core";
import {
schema,
standardComponentDefinitions,
standardActionDefinitions,
defineRegistry,
Renderer,
JSONUIProvider,
} from "@json-render/ink";
const catalog = defineCatalog(schema, {
components: { ...standardComponentDefinitions },
actions: standardActionDefinitions,
});
const { registry } = defineRegistry(catalog, { components: {} });
const spec = {
root: "card-1",
elements: {
"card-1": {
type: "Card",
props: { title: "Status" },
children: ["status-1"],
},
"status-1": {
type: "StatusLine",
props: { label: "Build", status: "success" },
children: [],
},
},
};
;
```
## 功能
### 流式传输 (SpecStream)
渐进式流式传输 AI 响应:
```
import { createSpecStreamCompiler } from "@json-render/core";
const compiler = createSpecStreamCompiler();
// Process chunks as they arrive
const { result, newPatches } = compiler.push(chunk);
setSpec(result); // Update UI with partial result
// Get final result
const finalSpec = compiler.getResult();
```
### AI Prompt 生成
根据你的目录生成系统 prompts:
```
const systemPrompt = catalog.prompt();
// Includes component descriptions, props schemas, available actions
```
### 条件可见性
```
{
"type": "Alert",
"props": { "message": "Error occurred" },
"visible": [
{ "$state": "/form/hasError" },
{ "$state": "/form/errorDismissed", "not": true }
]
}
```
### 动态 Props
任何 prop 值都可以使用表达式实现数据驱动:
```
{
"type": "Icon",
"props": {
"name": {
"$cond": { "$state": "/activeTab", "eq": "home" },
"$then": "home",
"$else": "home-outline"
},
"color": {
"$cond": { "$state": "/activeTab", "eq": "home" },
"$then": "#007AFF",
"$else": "#8E8E93"
}
}
}
```
表达式形式:
- **`{ "$state": "/state/key" }`** - 从状态模型中读取值
- **`{ "$cond": , "$then": , "$else": }`** - 评估条件并选择一个分支
- **`{ "$template": "Hello, ${/user/name}!" }`** - 将状态值插入到字符串中
- **`{ "$computed": "fn", "args": { ... } }`** - 使用解析后的参数调用已注册的函数
### 操作
组件可以触发操作,包括内置的 `setState` 操作:
```
{
"type": "Pressable",
"props": {
"action": "setState",
"actionParams": { "statePath": "/activeTab", "value": "home" }
},
"children": ["home-icon"]
}
```
`setState` 操作会直接更新状态模型,从而重新评估可见性条件和动态 prop 表达式。
### 状态监听器
通过触发操作来响应状态变化:
```
{
"type": "Select",
"props": {
"value": { "$bindState": "/form/country" },
"options": ["US", "Canada", "UK"]
},
"watch": {
"/form/country": {
"action": "loadCities",
"params": { "country": { "$state": "/form/country" } }
}
}
}
```
`watch` 是元素上的顶级字段(与 `type`/`props`/`children` 同级)。监听器会在监听的值发生变化时触发,而不是在初始渲染时触发。
## 演示
```
git clone https://github.com/vercel-labs/json-render
cd json-render
pnpm install
pnpm dev
```
- http://json-render.localhost:1355 - 文档与体验场
- http://dashboard-demo.json-render.localhost:1355 - 仪表盘示例
- http://react-email-demo.json-render.localhost:1355 - React Email 示例
- http://remotion-demo.json-render.localhost:1355 - Remotion 视频示例
- 聊天示例:在 `examples/chat` 中运行 `pnpm dev`
- Svelte 示例:在 `examples/svelte` 或 `examples/svelte-chat` 中运行 `pnpm dev`
- Vue 示例:在 `examples/vue` 中运行 `pnpm dev`
- Vite 渲染器 (React + Vue + Svelte + Solid):在 `examples/vite-renderers` 中运行 `pnpm dev`
- React Native 示例:在 `examples/react-native` 中运行 `npx expo start`
- 高斯溅射 (R3F):在 `examples/react-three-fiber-gsplat` 中运行 `pnpm dev`
- 高斯溅射(实验性独立 gsplat.js 演示):在 `examples/gsplat` 中运行 `pnpm dev`
## 工作原理
```
flowchart LR
A[User Prompt] --> B[AI + Catalog]
B --> C[JSON Spec]
C --> D[Renderer]
B -.- E([guardrailed])
C -.- F([predictable])
D -.- G([streamed])
```
1. **定义护栏** - AI 可以使用哪些组件、操作和数据绑定
2. **提示词** - 用自然语言描述你的需求
3. **AI 生成 JSON** - 输出始终可预测,受限于你的目录
4. **快速渲染** - 随着模型的响应进行流式传输和渐进式渲染
## 许可证
Apache-2.0
{props.title}
{children}
{props.label}
{format(props.value, props.format)}
),
Button: ({ props, emit }) => (
),
},
});
```
### 3. 渲染 AI 生成的规格说明
```
function Dashboard({ spec }) {
return {renderProps.children}
,
Button: (renderProps) => (
),
},
});
标签:React, Syscalls, UI组件, 大语言模型应用, 自动化攻击, 跨平台开发