Skip to content

json-render — Vercel Labs Generative UI framework

json-render is a Vercel Labs framework (Apache-2.0, @json-render/core on npm) for Generative UI: an LLM emits a JSON spec constrained to a developer-defined catalog of components + actions + data bindings, and a renderer materializes it into a live interface. The catalog acts as a typed action space — Zod-validated props schemas, enumerated actions, structured data bindings — and the same spec renders across 20+ target runtimes: React, Vue, Svelte, Solid, React Native, Next.js, TanStack Start, Three.js (including a GaussianSplat component), Remotion (video), React PDF, React Email, Ink (terminals), and Satori-based image/SVG output. Ships with 36 pre-built shadcn/ui components, a streaming compiler (SpecStream) that renders progressively as the model responds, a state model with dynamic prop expressions ($state, $cond, $template, $computed), state watchers, MCP integration for Claude/ChatGPT/Cursor/VS Code, and drop-in devtools panels per adapter.

  • The catalog defines the entire generative surface: components (Zod props schemas + descriptions), actions (named enumerated intents), and data bindings, from which catalog.prompt() derives a system prompt for the model [README §Quick Start, §AI Prompt Generation].
  • Output is a flat spec with a root element ID and an elements map keyed by ID, where each element carries type / props / children (plus optional visible, watch) — parseable as pure JSON [README §Renderers/React].
  • Any prop value can be a data-driven expression from a fixed vocabulary: { "$state": "/path" }, { "$cond": …, "$then": …, "$else": … }, { "$template": "…${/path}…" }, { "$computed": "fn", args: … } [README §Dynamic Props].
  • Components trigger actions via a setState primitive plus named catalog actions; state changes re-evaluate visibility conditions and dynamic expressions, and top-level watch fields let a state change trigger further actions [README §Actions, §State Watchers].
  • The same catalog + spec renders across host runtimes without spec changes — React, Vue 3, Svelte 5 (runes), Solid (fine-grained), React Native, Next.js (full apps with layouts / routes / SSR / metadata), TanStack Start, React Three Fiber (20 built-in 3D components including GaussianSplat), Remotion (timeline schema for video), React PDF, React Email, Ink (TUIs), and Satori (SVG/PNG) [README §Packages, §Renderers].
  • The SpecStream compiler consumes model chunks incrementally and yields partial specs + patch sets, so the UI updates as the model responds rather than after the final token [README §Streaming].
  • Component adherence is the trust contract: the AI can only reference components declared in the catalog, so output is guardrailed at the schema level rather than by post-hoc validation [README §Why json-render?].
  • Ships 36 pre-built shadcn/ui components (Radix UI + Tailwind) via @json-render/shadcn, with a Svelte-5 counterpart via @json-render/shadcn-svelte, and a companion @json-render/directives package of pre-built expression directives ($format, $math, $concat, $count, $truncate, $pluralize, $join, $t for i18n) [README §Packages].
  • MCP integration (@json-render/mcp) exposes the framework to Claude, ChatGPT, Cursor, and VS Code as MCP Apps, and adapters exist for Redux / Zustand / Jotai / XState as external state stores [README §Packages].
  • Devtools panel is framework-agnostic: spec tree, state editor, action log, stream log, catalog browser, DOM picker; drop-in adapters for React / Vue / Svelte / Solid, tree-shakes to null in production [README §Devtools].

The framework factors the generative-UI problem into four stable pieces:

  1. Catalog (defineCatalog(schema, { components, actions })) — the typed action space the model is allowed to emit. Component definitions bind a name to a Zod props schema plus a natural-language description; actions are named intents with descriptions. catalog.prompt() turns this into a system prompt containing the schemas and descriptions, so schema updates propagate to the LLM system prompt automatically.
  2. Registry (defineRegistry(catalog, { components })) — the host-runtime binding that maps each component name to a concrete implementation (React function, Vue render function, Svelte snippet, etc.). One catalog, many registries.
  3. Spec — a flat JSON document with root: string and elements: Record<id, { type, props, children }>. Elements can carry visible: [predicate…] and watch: { "/state/path": { action, params } }. Prop values can be literals or one of the expression forms.
  4. Renderer + SpecStream — a framework-appropriate <Renderer spec={spec} registry={registry} /> component evaluates dynamic expressions against a state model, subscribes children to state changes, and dispatches action callbacks; createSpecStreamCompiler() incrementally builds the spec from streaming model output and emits patches so the UI updates progressively.

The Next.js and TanStack Start adapters lift the spec above a single view: a NextAppSpec / StartAppSpec carries metadata, layouts, and routes maps whose values are specs, so the model can emit an entire multi-route app (with SSR, generateMetadata, generateStaticParams) as one JSON document. The Remotion adapter reshapes the spec into a composition + tracks + clips + audio timeline; the image adapter reuses the same tree through Satori to produce SVG/PNG.

The repo is a framework release, not a benchmarked system, so there are no evaluation numbers. Concrete artifacts:

  • 20+ published npm packages under @json-render/* (core, per-framework adapters, per-target renderers, devtools, state-store adapters, MCP, YAML wire format, codegen).
  • 36 pre-built shadcn/ui + 36 shadcn-svelte components ready to drop into a catalog.
  • Six example apps in the repo (docs playground, dashboard demo, React Email demo, Remotion video demo, chat example, Svelte examples, Vue examples, Vite renderers demo, React Native / Expo, R3F Gaussian-splat demo, standalone gsplat.js demo).
  • An experimental Jev composition mode (experimental_composeSpec / experimental_createEvaluator) surfaced in the playground under a feature flag; unreleased.

No claimed benchmarks against alternatives (LangChain UI, Vercel AI SDK Generative UI, Assistant UI, etc.), no throughput / latency numbers, no coverage measurements of catalog-vs-model adherence — this is a framework release, not a paper.

Sits adjacent to Tool-Use Agents at a different level of the stack: where the Tool-Use Agents concept has converged on structured function calls as the model’s action space (with SimpleTool: Parallel Decoding for Real-Time LLM Function Calling SimpleTool and Introducing System One Models & Jev Jev pushing calibrated typed-output decoding into the model itself), json-render treats the user interface as the structured output surface — one JSON tree per user turn, guardrailed by a Zod-validated component catalog, streamable, and portable across web / native / video / PDF / email / terminal targets. The catalog-as-action-space + catalog.prompt() pattern is the same trust-contract idea the tool-use recipes rely on (a typed schema the model is constrained to, not politely asked to follow), extended from callable actions to renderable structure.

The multi-renderer + streaming-partial-tree design shifts the interesting engineering question from “can the model produce valid JSON?” (largely solved by schema-constrained decoding, cf. Jev’s mathematical-guarantee framing) to “how much application state is expressible as pure projections over a spec?” — the same question Agent architecture as event log + typed components — actors composed with Tardigrade and Effect‘s “everything is f(log)” agent-architecture sketch raises for the event log, but applied to UI state instead of agent state (dynamic props + state watchers + visible predicates give a small algebra for expressing conditional UI without emitting a new spec each turn). Concrete framework instance of the Generative UI paradigm that a Luma-side agent surface (assistant that shows a live dashboard / storyboard / 3D scene / video timeline in response to a query) would want to build against or adapt.