JSON-Render: Guarded AI‑Prompted UI Library for React
JSON‑Render: Guarded AI‑Prompted UI Library for React
In the age of large language models, developers are constantly looking for ways to turn pure text prompts into actionable user interfaces. JSON‑Render fills that niche by giving AI a predictable and safe vocabulary so that the output is always a JSON tree you can render with React — no surprises, no security holes.
What is JSON‑Render?
JSON‑Render is a lightweight, TypeScript‑first toolkit consisting of two NPM packages:
- @json-render/core – schema definitions, guardrails, visibility logic, and action handlers.
- @json-render/react – React components, hooks, and renderers that turn the JSON into a live UI.
Key benefits:
| Feature | Why it matters |
|---|---|
| Guardrailed | The AI is restricted to a catalog of allowed component types, preventing malicious code injection. |
| Predictable | The JSON follows a Zod schema. Every property can be type‑checked at run time. |
| Fast | The stream can be rendered as the model sends data, meaning users see updates in real time. |
| Composable | Combine your own React components with guardrails; the library is agnostic to styling. |
Quick Start Guide
Below is a step‑by‑step walkthrough of a minimal example. Assume a fresh Node project with pnpm.
# Clone and bootstrap
git clone https://github.com/vercel-labs/json-render
cd json-render
pnpm install
pnpm dev
The dev command launches four services: *
localhost:3000– Docs & Playground *localhost:3001– Example dashboard app * Websocket endpoints for the API * A local ChatGPT‑compatible backend (see the repo for custom LLM integration)
1. Define the Catalog
In packages/core/src/catalog.ts, you declare every component the AI can use. Here’s a compact example:
import { createCatalog } from '@json-render/core'
import { z } from 'zod'
const catalog = createCatalog({
components: {
Card: {
props: z.object({ title: z.string() }),
hasChildren: true,
},
Metric: {
props: z.object({
label: z.string(),
valuePath: z.string(),
format: z.enum(['currency', 'percent', 'number']),
}),
},
Button: {
props: z.object({ label: z.string(), action: ActionSchema }),
},
},
actions: {
export_report: { description: 'Export dashboard to PDF' },
refresh_data: { description: 'Refresh all metrics' },
},
})
export default catalog
ActionSchemais a Zod schema you import from@json-render/corethat validates the shape of anactionpayload.
2. Register React Renderers
Create a small registry that maps component names to actual React elements.
const registry = {
Card: ({ element, children }) => (
<div className="card">
<h3>{element.props.title}</h3>
{children}
</div>
),
Metric: ({ element }) => {
const value = useDataValue(element.props.valuePath)
return <div className="metric">{format(value)}</div>
},
Button: ({ element, onAction }) => (
<button onClick={() => onAction(element.props.action)}>
{element.props.label}
</button>
),
}
3. Hook It All Together
import { useUIStream, DataProvider, ActionProvider, Renderer } from '@json-render/react'
function Dashboard() {
const { tree, send } = useUIStream({ api: '/api/generate' })
return (
<DataProvider initialData={{ revenue: 125000, growth: 0.15 }}>
<ActionProvider actions={{
export_report: () => downloadPDF(),
refresh_data: () => refetch(),
}}>
<input
placeholder="Create a revenue dashboard…"
onKeyDown={(e) => e.key === 'Enter' && send(e.target.value)}
/>
<Renderer tree={tree} components={registry} />
</ActionProvider>
</DataProvider>
)
}
When a user types a natural‑language prompt and hits Enter, the library streams the AI’s JSON response, the registry renders it, and any defined actions trigger callback functions.
Advanced Features
Conditional Visibility
Show or hide elements based on data, authentication, or custom logic:
{
"type": "Alert",
"props": { "message": "Error occurred" },
"visible": { "and": [ { "path": "/form/hasError" }, { "not": { "path": "/form/errorDismissed" } } ] }
}
Rich Actions with Confirmation
{
"type": "Button",
"props": {
"label": "Refund Payment",
"action": {
"name": "refund",
"params": { "paymentId": { "path": "/selected/id" } },
"confirm": { "title": "Confirm Refund", "variant": "danger" }
}
}
}
Built‑in Validation
{
"type": "TextField",
"props": {
"label": "Email",
"valuePath": "/form/email",
"checks": [
{ "fn": "required", "message": "Email is required" },
{ "fn": "email", "message": "Invalid email" }
],
"validateOn": "blur"
}
}
When to Use JSON‑Render
- Rapid prototyping – Turn prompts into functional dashboards in minutes.
- Decoupled UI generation – Separate your design system from the LLM.
- Secure UI rendering – Guardrails prevent arbitrary component injection.
- Multiplatform – The core can be used in React, React‑Native, or any UI framework that consumes JSON.
Getting Started
Clone the repo, add your own LLM backend or use the provided OpenAI adapter, and run pnpm dev. Your new React project will automatically import the rendering logic. Feel free to extend the catalog or replace the registry with stylized components from your design system.
Tip – Use the built‑in docs & playground at
localhost:3000to experiment with prompts, catalog changes, and live preview without touching code.
Conclusion
JSON‑Render gives developers a clean, type‑safe way to let LLMs generate UI components. By keeping the output in a predictable, guarded JSON format, you maintain control over security, performance, and user experience—all while offering the powerful convenience of natural‑language UI design. Ready to bring AI‑generated dashboards into your product? Grab the package from NPM and start experimenting today.