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 }) => (

{props.title}

{children}
), Metric: ({ props }) => (
{props.label} {format(props.value, props.format)}
), Button: ({ props, emit }) => ( ), }, }); ``` ### 3. 渲染 AI 生成的规格说明 ``` function Dashboard({ spec }) { return ; } ``` **就这么简单。** 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) =>
{renderProps.children}
, Button: (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
标签:React, Syscalls, UI组件, 大语言模型应用, 自动化攻击, 跨平台开发