JSON Render
Bring your own component library. Have the agent emit a JSON spec and render allowed component types through a typed React registry.
Open your coding agent in your project's folder, or in an empty folder for a new app.This runs in a coding agent on your computer.
This feature (declarative-json-render) hasn't been tagged in any CrewAI Flows cell yet. Try CopilotKit's Built-in Agent, LangGraph (Python), LangGraph (TypeScript).
You have a chat surface and you want the agent to draw a dashboard from a typed JSON spec. By the end of this guide, the agent will emit a { root, elements } object, and @json-render/react will render it through your React component registry.
When to use this#
- Structured UI with a typed contract where the renderer accepts known component types.
- Tolerance for prose preamble + code fences in the agent's output (the demo's parser handles them).
- Cases where you already use json-render elsewhere or prefer a Zod-described catalog.
If you'd rather have a streaming progressive render than a one-shot JSON render, see the sibling page Hashbrown for the same scenario with @hashbrownai/react.
Frontend#
The integration point is <CopilotChat>'s messageView.assistantMessage slot. The demo's Code tab has the complete framework-specific files, and the Markdown version of this page includes the key frontend source files. These demos use @json-render/react and @json-render/core version 0.18.0 with Zod 4 for the catalog. Check the selected demo's package.json in the repository for later version changes. Demos that also use Zod 3 import Zod 4 through the zod4 package alias. page.tsx provides <CopilotKit>, chat.tsx installs JsonRenderAssistantMessage in the chat slot, and json-render-renderer.tsx parses the assistant content. The Code tab also shows the runtime route for the selected framework.
The renderer imports registry from registry.tsx, wraps <Renderer spec={spec} registry={registry} /> in <JSONUIProvider registry={registry}>, and falls back to the normal assistant message until it can parse a spec. catalog.ts defines component names and Zod prop schemas with defineCatalog; registry.tsx uses defineRegistry(catalog, …) to bind those names to React components. Copy both files with the renderer. The demo's parseSpec allowlists component types and checks the basic object shape, but does not validate emitted props against the Zod schemas at runtime. Validate props before rendering if you rely on that guarantee.
Backend#
The agent emits a { root, elements } JSON object as the assistant message content. root references a top-level element id; elements maps each id to a { type, props, children } triple matching the catalog.
{
"root": "revenue-card",
"elements": {
"revenue-card": {
"type": "MetricCard",
"props": { "label": "Total revenue", "value": "$184,302", "trend": "+12%" },
"children": ["by-region"]
},
"by-region": {
"type": "BarChart",
"props": {
"title": "Revenue by region",
"description": null,
"data": [
{ "label": "North America", "value": 112000 },
{ "label": "EMEA", "value": 72302 }
]
}
}
}
}The example uses a MetricCard root and its registered BarChart child. The renderer tolerates a prose preamble or code fence around the JSON, but the agent prompts ask for plain JSON.
Comparing the two patterns#
Both byoc-json-render and byoc-hashbrown solve the same problem with two different rendering libraries. The agent contract is similar; the React glue, validation strategy, and rendering behaviour differ.