{
  "markdown": "# json-render\n\n**The Generative UI framework.**\n\nGenerate dynamic, personalized UIs from prompts without sacrificing reliability. Predefined components and actions for safe, predictable output.\n\n```bash\n# for React\nnpm install @json-render/core @json-render/react\n# for React with pre-built shadcn/ui components\nnpm install @json-render/shadcn\n# or for React Native\nnpm install @json-render/core @json-render/react-native\n# or for video\nnpm install @json-render/core @json-render/remotion\n# or for PDF documents\nnpm install @json-render/core @json-render/react-pdf\n# or for HTML email\nnpm install @json-render/core @json-render/react-email @react-email/components @react-email/render\n# or for Vue\nnpm install @json-render/core @json-render/vue\n# or for Svelte\nnpm install @json-render/core @json-render/svelte\n# or for SolidJS\nnpm install @json-render/core @json-render/solid\n# or for terminal UIs\nnpm install @json-render/core @json-render/ink ink react\n# or for full Next.js apps (routes, layouts, SSR, metadata)\nnpm install @json-render/core @json-render/react @json-render/next\n# or for 3D scenes (and gaussian splatting via the GaussianSplat component)\nnpm install @json-render/core @json-render/react-three-fiber @react-three/fiber @react-three/drei three\n```\n\n## Why json-render?\n\njson-render is a **Generative UI** framework: AI generates interfaces from natural language prompts, constrained to components you define. You set the guardrails, AI generates within them:\n\n- **Guardrailed** - AI can only use components in your catalog\n- **Predictable** - JSON output matches your schema, every time\n- **Fast** - Stream and render progressively as the model responds\n- **Cross-Platform** - React, Vue, Svelte, Solid (web), React Native (mobile) from the same catalog\n- **Batteries Included** - 36 pre-built shadcn/ui components ready to use\n\n## Quick Start\n\n### 1. Define Your Catalog\n\n```typescript\nimport { defineCatalog } from \"@json-render/core\";\nimport { schema } from \"@json-render/react/schema\";\nimport { z } from \"zod\";\n\nconst catalog = defineCatalog(schema, {\n  components: {\n    Card: {\n      props: z.object({ title: z.string() }),\n      description: \"A card container\",\n    },\n    Metric: {\n      props: z.object({\n        label: z.string(),\n        value: z.string(),\n        format: z.enum([\"currency\", \"percent\", \"number\"]).nullable(),\n      }),\n      description: \"Display a metric value\",\n    },\n    Button: {\n      props: z.object({\n        label: z.string(),\n        action: z.string(),\n      }),\n      description: \"Clickable button\",\n    },\n  },\n  actions: {\n    export_report: { description: \"Export dashboard to PDF\" },\n    refresh_data: { description: \"Refresh all metrics\" },\n  },\n});\n```\n\n### 2. Define Your Components\n\n```tsx\nimport { defineRegistry, Renderer } from \"@json-render/react\";\n\nconst { registry } = defineRegistry(catalog, {\n  components: {\n    Card: ({ props, children }) => (\n      <div className=\"card\">\n        <h3>{props.title}</h3>\n        {children}\n      </div>\n    ),\n    Metric: ({ props }) => (\n      <div className=\"metric\">\n        <span>{props.label}</span>\n        <span>{format(props.value, props.format)}</span>\n      </div>\n    ),\n    Button: ({ props, emit }) => (\n      <button onClick={() => emit(\"press\")}>{props.label}</button>\n    ),\n  },\n});\n```\n\n### 3. Render AI-Generated Specs\n\n```tsx\nfunction Dashboard({ spec }) {\n  return <Renderer spec={spec} registry={registry} />;\n}\n```\n\n**That's it.** AI generates JSON, you render it safely.\n\n---\n\n## Packages\n\n| Package                     | Description                                                            |\n| --------------------------- | ---------------------------------------------------------------------- |\n| `@json-render/core`         | Schemas, catalogs, AI prompts, dynamic props, SpecStream utilities     |\n| `@json-render/react`        | React renderer, contexts, hooks                                        |\n| `@json-render/vue`          | Vue 3 renderer, composables, providers                                 |\n| `@json-render/svelte`       | Svelte 5 renderer with runes-based reactivity                          |\n| `@json-render/solid`        | SolidJS renderer with fine-grained reactive contexts                   |\n| `@json-render/shadcn`       | 36 pre-built shadcn/ui components (Radix UI + Tailwind CSS)            |\n| `@json-render/shadcn-svelte`| 36 pre-built shadcn-svelte components (Svelte 5 + Tailwind CSS)        |\n| `@json-render/react-three-fiber` | React Three Fiber renderer for 3D scenes (20 built-in components, including GaussianSplat)  |\n| `@json-render/react-native` | React Native renderer with standard mobile components                  |\n| `@json-render/next`         | Next.js renderer — JSON becomes full apps with routes, layouts, SSR    |\n| `@json-render/remotion`     | Remotion video renderer, timeline schema                               |\n| `@json-render/react-pdf`    | React PDF renderer for generating PDF documents from specs             |\n| `@json-render/react-email`  | React Email renderer for HTML/plain-text emails from specs             |\n| `@json-render/ink`          | Ink terminal renderer with built-in components for interactive TUIs.   |\n| `@json-render/image`        | Image renderer for SVG/PNG output (OG images, social cards) via Satori |\n| `@json-render/directives`   | Pre-built custom directives — $format, $math, $concat, $count, $truncate, $pluralize, $join, $t (i18n) |\n| `@json-render/codegen`      | Utilities for generating code from json-render UI trees                |\n| `@json-render/devtools`     | Framework-agnostic devtools core — panel UI, event store, picker, stream taps |\n| `@json-render/devtools-react`   | React adapter for `@json-render/devtools` (drop-in `<JsonRenderDevtools />`)     |\n| `@json-render/devtools-vue`     | Vue adapter for `@json-render/devtools`                                           |\n| `@json-render/devtools-svelte`  | Svelte adapter for `@json-render/devtools`                                        |\n| `@json-render/devtools-solid`   | SolidJS adapter for `@json-render/devtools`                                       |\n| `@json-render/redux`        | Redux / Redux Toolkit adapter for `StateStore`                         |\n| `@json-render/zustand`      | Zustand adapter for `StateStore`                                       |\n| `@json-render/jotai`        | Jotai adapter for `StateStore`                                         |\n| `@json-render/xstate`       | XState Store (atom) adapter for `StateStore`                           |\n| `@json-render/mcp`          | MCP Apps integration for Claude, ChatGPT, Cursor, VS Code              |\n| `@json-render/yaml`         | YAML wire format with streaming parser, edit modes, AI SDK transform   |\n\n## Renderers\n\n### React (UI)\n\n```tsx\nimport { defineRegistry, Renderer } from \"@json-render/react\";\nimport { schema } from \"@json-render/react/schema\";\n\n// Flat spec format (root key + elements map)\nconst spec = {\n  root: \"card-1\",\n  elements: {\n    \"card-1\": {\n      type: \"Card\",\n      props: { title: \"Hello\" },\n      children: [\"button-1\"],\n    },\n    \"button-1\": {\n      type: \"Button\",\n      props: { label: \"Click me\" },\n      children: [],\n    },\n  },\n};\n\n// defineRegistry creates a type-safe component registry\nconst { registry } = defineRegistry(catalog, { components });\n<Renderer spec={spec} registry={registry} />;\n```\n\n### Vue (UI)\n\n```typescript\nimport { h } from \"vue\";\nimport { defineRegistry, Renderer } from \"@json-render/vue\";\nimport { schema } from \"@json-render/vue/schema\";\n\nconst { registry } = defineRegistry(catalog, {\n  components: {\n    Card: ({ props, children }) =>\n      h(\"div\", { class: \"card\" }, [h(\"h3\", null, props.title), children]),\n    Button: ({ props, emit }) =>\n      h(\"button\", { onClick: () => emit(\"press\") }, props.label),\n  },\n});\n\n// In your Vue component template:\n// <Renderer :spec=\"spec\" :registry=\"registry\" />\n```\n\n### Svelte (UI)\n\n```typescript\nimport { defineRegistry, Renderer } from \"@json-render/svelte\";\nimport { schema } from \"@json-render/svelte/schema\";\n\nconst { registry } = defineRegistry(catalog, {\n  components: {\n    Card: ({ props, children }) => /* Svelte 5 snippet */,\n    Button: ({ props, emit }) => /* Svelte 5 snippet */,\n  },\n});\n\n// In your Svelte component:\n// <Renderer spec={spec} registry={registry} />\n```\n\n### Solid (UI)\n\n```tsx\nimport { defineRegistry, Renderer } from \"@json-render/solid\";\nimport { schema } from \"@json-render/solid/schema\";\n\nconst { registry } = defineRegistry(catalog, {\n  components: {\n    Card: (renderProps) => <div>{renderProps.children}</div>,\n    Button: (renderProps) => (\n      <button onClick={() => renderProps.emit(\"press\")}>\n        {renderProps.element.props.label as string}\n      </button>\n    ),\n  },\n});\n\n<Renderer spec={spec} registry={registry} />;\n```\n\n### shadcn/ui (Web)\n\n```tsx\nimport { defineCatalog } from \"@json-render/core\";\nimport { schema } from \"@json-render/react/schema\";\nimport { defineRegistry, Renderer } from \"@json-render/react\";\nimport { shadcnComponentDefinitions } from \"@json-render/shadcn/catalog\";\nimport { shadcnComponents } from \"@json-render/shadcn\";\n\n// Pick components from the 36 standard definitions\nconst catalog = defineCatalog(schema, {\n  components: {\n    Card: shadcnComponentDefinitions.Card,\n    Stack: shadcnComponentDefinitions.Stack,\n    Heading: shadcnComponentDefinitions.Heading,\n    Button: shadcnComponentDefinitions.Button,\n  },\n  actions: {},\n});\n\n// Use matching implementations\nconst { registry } = defineRegistry(catalog, {\n  components: {\n    Card: shadcnComponents.Card,\n    Stack: shadcnComponents.Stack,\n    Heading: shadcnComponents.Heading,\n    Button: shadcnComponents.Button,\n  },\n});\n\n<Renderer spec={spec} registry={registry} />;\n```\n\n### React Native (Mobile)\n\n```tsx\nimport { defineCatalog } from \"@json-render/core\";\nimport { schema } from \"@json-render/react-native/schema\";\nimport {\n  standardComponentDefinitions,\n  standardActionDefinitions,\n} from \"@json-render/react-native/catalog\";\nimport { defineRegistry, Renderer } from \"@json-render/react-native\";\n\n// 25+ standard components included\nconst catalog = defineCatalog(schema, {\n  components: { ...standardComponentDefinitions },\n  actions: standardActionDefinitions,\n});\n\nconst { registry } = defineRegistry(catalog, { components: {} });\n<Renderer spec={spec} registry={registry} />;\n```\n\n### Remotion (Video)\n\n```tsx\nimport { Player } from \"@remotion/player\";\nimport {\n  Renderer,\n  schema,\n  standardComponentDefinitions,\n} from \"@json-render/remotion\";\n\n// Timeline spec format\nconst spec = {\n  composition: {\n    id: \"video\",\n    fps: 30,\n    width: 1920,\n    height: 1080,\n    durationInFrames: 300,\n  },\n  tracks: [{ id: \"main\", name: \"Main\", type: \"video\", enabled: true }],\n  clips: [\n    {\n      id: \"clip-1\",\n      trackId: \"main\",\n      component: \"TitleCard\",\n      props: { title: \"Hello\" },\n      from: 0,\n      durationInFrames: 90,\n    },\n  ],\n  audio: { tracks: [] },\n};\n\n<Player\n  component={Renderer}\n  inputProps={{ spec }}\n  durationInFrames={spec.composition.durationInFrames}\n  fps={spec.composition.fps}\n  compositionWidth={spec.composition.width}\n  compositionHeight={spec.composition.height}\n/>;\n```\n\n### React PDF (Documents)\n\n```typescript\nimport { renderToBuffer } from \"@json-render/react-pdf\";\n\nconst spec = {\n  root: \"doc\",\n  elements: {\n    doc: {\n      type: \"Document\",\n      props: { title: \"Invoice\" },\n      children: [\"page-1\"],\n    },\n    \"page-1\": {\n      type: \"Page\",\n      props: { size: \"A4\" },\n      children: [\"heading-1\", \"table-1\"],\n    },\n    \"heading-1\": {\n      type: \"Heading\",\n      props: { text: \"Invoice #1234\", level: \"h1\" },\n      children: [],\n    },\n    \"table-1\": {\n      type: \"Table\",\n      props: {\n        columns: [\n          { header: \"Item\", width: \"60%\" },\n          { header: \"Price\", width: \"40%\", align: \"right\" },\n        ],\n        rows: [\n          [\"Widget A\", \"$10.00\"],\n          [\"Widget B\", \"$25.00\"],\n        ],\n      },\n      children: [],\n    },\n  },\n};\n\n// Render to buffer, stream, or file\nconst buffer = await renderToBuffer(spec);\n```\n\n### React Email (Email)\n\n```typescript\nimport { renderToHtml } from \"@json-render/react-email\";\nimport { schema, standardComponentDefinitions } from \"@json-render/react-email\";\nimport { defineCatalog } from \"@json-render/core\";\n\nconst catalog = defineCatalog(schema, {\n  components: standardComponentDefinitions,\n});\n\nconst spec = {\n  root: \"html-1\",\n  elements: {\n    \"html-1\": {\n      type: \"Html\",\n      props: { lang: \"en\", dir: \"ltr\" },\n      children: [\"head-1\", \"body-1\"],\n    },\n    \"head-1\": { type: \"Head\", props: {}, children: [] },\n    \"body-1\": {\n      type: \"Body\",\n      props: { style: { backgroundColor: \"#f6f9fc\" } },\n      children: [\"container-1\"],\n    },\n    \"container-1\": {\n      type: \"Container\",\n      props: {\n        style: { maxWidth: \"600px\", margin: \"0 auto\", padding: \"20px\" },\n      },\n      children: [\"heading-1\", \"text-1\"],\n    },\n    \"heading-1\": { type: \"Heading\", props: { text: \"Welcome\" }, children: [] },\n    \"text-1\": {\n      type: \"Text\",\n      props: { text: \"Thanks for signing up.\" },\n      children: [],\n    },\n  },\n};\n\nconst html = await renderToHtml(spec);\n```\n\n### Image (SVG/PNG)\n\n```typescript\nimport { renderToPng } from \"@json-render/image/render\";\n\nconst spec = {\n  root: \"frame\",\n  elements: {\n    frame: {\n      type: \"Frame\",\n      props: { width: 1200, height: 630, backgroundColor: \"#1a1a2e\" },\n      children: [\"heading\"],\n    },\n    heading: {\n      type: \"Heading\",\n      props: { text: \"Hello World\", level: \"h1\", color: \"#ffffff\" },\n      children: [],\n    },\n  },\n};\n\n// Render to PNG (requires @resvg/resvg-js)\nconst png = await renderToPng(spec, { fonts });\n\n// Or render to SVG string\nimport { renderToSvg } from \"@json-render/image/render\";\nconst svg = await renderToSvg(spec, { fonts });\n```\n\n### Three.js (3D)\n\n```tsx\nimport { defineCatalog } from \"@json-render/core\";\nimport { schema, defineRegistry } from \"@json-render/react\";\nimport {\n  threeComponentDefinitions,\n  threeComponents,\n  ThreeCanvas,\n} from \"@json-render/react-three-fiber\";\n\nconst catalog = defineCatalog(schema, {\n  components: {\n    Box: threeComponentDefinitions.Box,\n    Sphere: threeComponentDefinitions.Sphere,\n    AmbientLight: threeComponentDefinitions.AmbientLight,\n    DirectionalLight: threeComponentDefinitions.DirectionalLight,\n    GaussianSplat: threeComponentDefinitions.GaussianSplat,\n    OrbitControls: threeComponentDefinitions.OrbitControls,\n  },\n  actions: {},\n});\n\nconst { registry } = defineRegistry(catalog, {\n  components: {\n    Box: threeComponents.Box,\n    Sphere: threeComponents.Sphere,\n    AmbientLight: threeComponents.AmbientLight,\n    DirectionalLight: threeComponents.DirectionalLight,\n    GaussianSplat: threeComponents.GaussianSplat,\n    OrbitControls: threeComponents.OrbitControls,\n  },\n});\n\n<ThreeCanvas\n  spec={spec}\n  registry={registry}\n  shadows\n  camera={{ position: [5, 5, 5], fov: 50 }}\n  style={{ width: \"100%\", height: \"100vh\" }}\n/>;\n```\n\n### Next.js (Full Apps)\n\n```typescript\nimport type { NextAppSpec } from \"@json-render/next\";\nimport { createNextApp } from \"@json-render/next/server\";\nimport { NextAppProvider } from \"@json-render/next\";\n\nconst spec: NextAppSpec = {\n  metadata: { title: { default: \"My App\", template: \"%s | My App\" } },\n  layouts: {\n    main: {\n      root: \"shell\",\n      elements: {\n        shell: { type: \"Container\", props: {}, children: [\"nav\", \"slot\"] },\n        nav: { type: \"NavBar\", props: {}, children: [] },\n        slot: { type: \"Slot\", props: {}, children: [] },\n      },\n    },\n  },\n  routes: {\n    \"/\": {\n      layout: \"main\",\n      metadata: { title: \"Home\" },\n      page: {\n        root: \"hero\",\n        elements: {\n          hero: { type: \"Card\", props: { title: \"Welcome\" }, children: [] },\n        },\n      },\n    },\n  },\n};\n\n// Server: creates Page, generateMetadata, generateStaticParams\nconst app = createNextApp({ spec });\n\n// Client: wrap your layout with NextAppProvider\n// <NextAppProvider registry={registry} handlers={handlers}>\n//   {children}\n// </NextAppProvider>\n```\n\n### shadcn-svelte (Svelte)\n\n```typescript\nimport { defineCatalog } from \"@json-render/core\";\nimport { schema } from \"@json-render/svelte/schema\";\nimport { defineRegistry, Renderer } from \"@json-render/svelte\";\nimport { shadcnComponentDefinitions } from \"@json-render/shadcn-svelte/catalog\";\nimport { shadcnComponents } from \"@json-render/shadcn-svelte\";\n\nconst catalog = defineCatalog(schema, {\n  components: {\n    Card: shadcnComponentDefinitions.Card,\n    Stack: shadcnComponentDefinitions.Stack,\n    Heading: shadcnComponentDefinitions.Heading,\n    Button: shadcnComponentDefinitions.Button,\n  },\n  actions: {},\n});\n\nconst { registry } = defineRegistry(catalog, {\n  components: {\n    Card: shadcnComponents.Card,\n    Stack: shadcnComponents.Stack,\n    Heading: shadcnComponents.Heading,\n    Button: shadcnComponents.Button,\n  },\n});\n\n// In your Svelte component:\n// <Renderer spec={spec} registry={registry} />\n```\n\n### Devtools\n\nDrop-in inspector panel for any json-render app. Spec tree, state editor, action log, stream log, catalog browser, DOM picker.\n\n```tsx\n// React\nimport { JsonRenderDevtools } from \"@json-render/devtools-react\";\n\n<JSONUIProvider registry={registry} handlers={handlers}>\n  <Renderer spec={spec} registry={registry} />\n  <JsonRenderDevtools spec={spec} catalog={catalog} messages={messages} />\n</JSONUIProvider>;\n```\n\nFloating toggle appears bottom-right. Hotkey: `Ctrl`/`Cmd` + `Shift` + `J`. Tree-shakes to `null` in production.\n\nAvailable for React, Vue, Svelte, and Solid — swap `@json-render/devtools-react` for the adapter that matches your renderer.\n\n### Ink (Terminal)\n\n```tsx\nimport { defineCatalog } from \"@json-render/core\";\nimport {\n  schema,\n  standardComponentDefinitions,\n  standardActionDefinitions,\n  defineRegistry,\n  Renderer,\n  JSONUIProvider,\n} from \"@json-render/ink\";\n\nconst catalog = defineCatalog(schema, {\n  components: { ...standardComponentDefinitions },\n  actions: standardActionDefinitions,\n});\n\nconst { registry } = defineRegistry(catalog, { components: {} });\n\nconst spec = {\n  root: \"card-1\",\n  elements: {\n    \"card-1\": {\n      type: \"Card\",\n      props: { title: \"Status\" },\n      children: [\"status-1\"],\n    },\n    \"status-1\": {\n      type: \"StatusLine\",\n      props: { label: \"Build\", status: \"success\" },\n      children: [],\n    },\n  },\n};\n\n<JSONUIProvider initialState={{}}>\n  <Renderer spec={spec} registry={registry} />\n</JSONUIProvider>;\n```\n\n## Features\n\n### Streaming (SpecStream)\n\nStream AI responses progressively:\n\n```typescript\nimport { createSpecStreamCompiler } from \"@json-render/core\";\n\nconst compiler = createSpecStreamCompiler<MySpec>();\n\n// Process chunks as they arrive\nconst { result, newPatches } = compiler.push(chunk);\nsetSpec(result); // Update UI with partial result\n\n// Get final result\nconst finalSpec = compiler.getResult();\n```\n\n### AI Prompt Generation\n\nGenerate system prompts from your catalog:\n\n```typescript\nconst systemPrompt = catalog.prompt();\n// Includes component descriptions, props schemas, available actions\n```\n\n### Conditional Visibility\n\n```json\n{\n  \"type\": \"Alert\",\n  \"props\": { \"message\": \"Error occurred\" },\n  \"visible\": [\n    { \"$state\": \"/form/hasError\" },\n    { \"$state\": \"/form/errorDismissed\", \"not\": true }\n  ]\n}\n```\n\n### Dynamic Props\n\nAny prop value can be data-driven using expressions:\n\n```json\n{\n  \"type\": \"Icon\",\n  \"props\": {\n    \"name\": {\n      \"$cond\": { \"$state\": \"/activeTab\", \"eq\": \"home\" },\n      \"$then\": \"home\",\n      \"$else\": \"home-outline\"\n    },\n    \"color\": {\n      \"$cond\": { \"$state\": \"/activeTab\", \"eq\": \"home\" },\n      \"$then\": \"#007AFF\",\n      \"$else\": \"#8E8E93\"\n    }\n  }\n}\n```\n\nExpression forms:\n\n- **`{ \"$state\": \"/state/key\" }`** - reads a value from the state model\n- **`{ \"$cond\": <condition>, \"$then\": <value>, \"$else\": <value> }`** - evaluates a condition and picks a branch\n- **`{ \"$template\": \"Hello, ${/user/name}!\" }`** - interpolates state values into strings\n- **`{ \"$computed\": \"fn\", \"args\": { ... } }`** - calls a registered function with resolved args\n\n### Actions\n\nComponents can trigger actions, including the built-in `setState` action:\n\n```json\n{\n  \"type\": \"Pressable\",\n  \"props\": {\n    \"action\": \"setState\",\n    \"actionParams\": { \"statePath\": \"/activeTab\", \"value\": \"home\" }\n  },\n  \"children\": [\"home-icon\"]\n}\n```\n\nThe `setState` action updates the state model directly, which re-evaluates visibility conditions and dynamic prop expressions.\n\n### State Watchers\n\nReact to state changes by triggering actions:\n\n```json\n{\n  \"type\": \"Select\",\n  \"props\": {\n    \"value\": { \"$bindState\": \"/form/country\" },\n    \"options\": [\"US\", \"Canada\", \"UK\"]\n  },\n  \"watch\": {\n    \"/form/country\": {\n      \"action\": \"loadCities\",\n      \"params\": { \"country\": { \"$state\": \"/form/country\" } }\n    }\n  }\n}\n```\n\n`watch` is a top-level field on elements (sibling of `type`/`props`/`children`). Watchers fire when the watched value changes, not on initial render.\n\n---\n\n## Demo\n\n```bash\ngit clone https://github.com/vercel-labs/json-render\ncd json-render\npnpm install\npnpm dev\n```\n\n- http://json-render.localhost:1355 - Docs & Playground\n- http://dashboard-demo.json-render.localhost:1355 - Example Dashboard\n- http://react-email-demo.json-render.localhost:1355 - React Email Example\n- http://remotion-demo.json-render.localhost:1355 - Remotion Video Example\n- Chat Example: run `pnpm dev` in `examples/chat`\n- Svelte Example: run `pnpm dev` in `examples/svelte` or `examples/svelte-chat`\n- Vue Example: run `pnpm dev` in `examples/vue`\n- Vite Renderers (React + Vue + Svelte + Solid): run `pnpm dev` in `examples/vite-renderers`\n- React Native example: run `npx expo start` in `examples/react-native`\n- Gaussian Splatting (R3F): run `pnpm dev` in `examples/react-three-fiber-gsplat`\n- Gaussian Splatting (experimental standalone gsplat.js demo): run `pnpm dev` in `examples/gsplat`\n\n## How It Works\n\n```mermaid\nflowchart LR\n    A[User Prompt] --> B[AI + Catalog]\n    B --> C[JSON Spec]\n    C --> D[Renderer]\n\n    B -.- E([guardrailed])\n    C -.- F([predictable])\n    D -.- G([streamed])\n```\n\n1. **Define the guardrails** - what components, actions, and data bindings AI can use\n2. **Prompt** - describe what you want in natural language\n3. **AI generates JSON** - output is always predictable, constrained to your catalog\n4. **Render fast** - stream and render progressively as the model responds\n\n## License\n\nApache-2.0\n",
  "bytes": 22545,
  "sha": "4857ef5ba7ec96ded1105191f7c585c8057617d879e56a5cb571ee62663dca4e",
  "repo_slug": "vercel-labs/json-render",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/skl_vercel_labs_json_render_skill_creator_5708f15b/readme"
}