The Generative UI framework
The Generative UI framework.
Generate dynamic, personalized UIs from prompts without sacrificing reliability. Predefined components and actions for safe, predictable output.
…
json-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:
…
import { defineRegistry, Renderer } from "@json-render/react";
const { registry } = defineRegistry(catalog, {
components: {
Card: ({ props, children }) => (
),
Metric: ({ props }) => (
),
Button: ({ props, emit }) => (
<button onClick={() => emit("press")}>{props.label}</button>
),
},
});
function Dashboard({ spec }) {
return <Renderer spec={spec} registry={registry} />;
}
That's it. AI generates JSON, you render it safely.
@json-render/core
Schemas, catalogs, AI prompts, dynamic props, SpecStream utilities
@json-render/react
React renderer, contexts, hooks
@json-render/vue
Vue 3 renderer, composables, providers
@json-render/svelte
Svelte 5 renderer with runes-based reactivity
@json-render/solid
SolidJS renderer with fine-grained reactive contexts
@json-render/shadcn
36 pre-built shadcn/ui components (Radix UI + Tailwind CSS)
@json-render/shadcn-svelte
36 pre-built shadcn-svelte components (Svelte 5 + Tailwind CSS)
@json-render/react-three-fiber
React Three Fiber renderer for 3D scenes (20 built-in components, including GaussianSplat)
@json-render/react-native
React Native renderer with standard mobile components
@json-render/next
Next.js renderer — JSON becomes full apps with routes, layouts, SSR
@json-render/tanstack-start
TanStack Start renderer — full apps with routes, layouts, SSR, and head metadata
@json-render/remotion
Remotion video renderer, timeline schema
@json-render/react-pdf
React PDF renderer for generating PDF documents from specs
@json-render/react-email
React Email renderer for HTML/plain-text emails from specs
@json-render/ink
Ink terminal renderer with built-in components for interactive TUIs.
@json-render/image
Image renderer for SVG/PNG output (OG images, social cards) via Satori
@json-render/directives
Pre-built custom directives — $format, $math, $concat, $count, $truncate, $pluralize, $join, $t (i18n)
@json-render/codegen
Utilities for generating code from json-render UI trees
@json-render/devtools
Framework-agnostic devtools core — panel UI, event store, picker, stream taps
@json-render/devtools-react
React adapter for @json-render/devtools (drop-in <JsonRenderDevtools />)
@json-render/devtools-vue
Vue adapter for @json-render/devtools
@json-render/devtools-svelte
Svelte adapter for @json-render/devtools
@json-render/devtools-solid
SolidJS adapter for @json-render/devtools
@json-render/redux
Redux / Redux Toolkit adapter for StateStore
@json-render/zustand
Zustand adapter for StateStore
@json-render/jotai
Jotai adapter for StateStore
@json-render/xstate
XState Store (atom) adapter for StateStore
@json-render/mcp
MCP Apps integration for Claude, ChatGPT, Cursor, VS Code
@json-render/yaml
YAML wire format with streaming parser, edit modes, AI SDK transform
…
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:
// <Renderer :spec="spec" :registry="registry" />
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:
// <Renderer spec={spec} registry={registry} />
import { defineRegistry, Renderer } from "@json-render/solid";
import { schema } from "@json-render/solid/schema";
const { registry } = defineRegistry(catalog, {
components: {
Card: (renderProps) =>
,
Button: (renderProps) => (
<button onClick={() => renderProps.emit("press")}>
{renderProps.element.props.label as string}
</button>
),
},
});
<Renderer spec={spec} registry={registry} />;
…
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: {} });
<Renderer spec={spec} registry={registry} />;
…
…
…
…
…
…
…
Wrap the root route's outlet with <StartAppProvider spec={spec}> so route
fallback components can resolve the current route. Pass named $computed
implementations through its functions prop.
…
Drop-in inspector panel for any json-render app. Spec tree, state editor, action log, stream log, catalog browser, DOM picker.
// React
import { JsonRenderDevtools } from "@json-render/devtools-react";
<JSONUIProvider registry={registry} handlers={handlers}>
<Renderer spec={spec} registry={registry} />
<JsonRenderDevtools spec={spec} catalog={catalog} messages={messages} />
</JSONUIProvider>;
Floating toggle appears bottom-right. Hotkey: Ctrl/Cmd + Shift + J. Tree-shakes to null in production.
Available for React, Vue, Svelte, and Solid — swap @json-render/devtools-react for the adapter that matches your renderer.
…
Stream AI responses progressively:
import { createSpecStreamCompiler } from "@json-render/core";
const compiler = createSpecStreamCompiler<MySpec>();
// 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();
Generate system prompts from your catalog:
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 }
]
}
Any prop value can be data-driven using expressions:
{
"type": "Icon",
"props": {
"name": {
"$cond": { "$state": "/activeTab", "eq": "home" },
"$then": "home",
"$else": "home-outline"
},
"color": {
"$cond": { "$state": "/activeTab", "eq": "home" },
"$then": "#007AFF",
"$else": "#8E8E93"
}
}
}
Expression forms:
{ "$state": "/state/key" } - reads a value from the state model{ "$cond": <condition>, "$then": <value>, "$else": <value> } - evaluates a condition and picks a branch{ "$template": "Hello, ${/user/name}!" } - interpolates state values into strings{ "$computed": "fn", "args": { ... } } - calls a registered function with resolved argsComponents can trigger actions, including the built-in setState action:
{
"type": "Pressable",
"props": {
"action": "setState",
"actionParams": { "statePath": "/activeTab", "value": "home" }
},
"children": ["home-icon"]
}
The setState action updates the state model directly, which re-evaluates visibility conditions and dynamic prop expressions.
React to state changes by triggering actions:
{
"type": "Select",
"props": {
"value": { "$bindState": "/form/country" },
"options": ["US", "Canada", "UK"]
},
"watch": {
"/form/country": {
"action": "loadCities",
"params": { "country": { "$state": "/form/country" } }
}
}
}
watch is a top-level field on elements (sibling of type/props/children). Watchers fire when the watched value changes, not on initial render.
git clone https://github.com/vercel-labs/json-render
cd json-render
pnpm install
pnpm dev
pnpm dev in examples/chatpnpm dev in examples/svelte or examples/svelte-chatpnpm dev in examples/vuepnpm dev in examples/vite-renderersnpx expo start in examples/react-nativepnpm dev in examples/react-three-fiber-gsplatpnpm dev in examples/gsplatflowchart LR
A[User Prompt] --> B[AI + Catalog]
B --> C[JSON Spec]
C --> D[Renderer]
B -.- E([guardrailed])
C -.- F([predictable])
D -.- G([streamed])