From a455825a25da2dae3a7b4a6f350de35e8e39eb9f Mon Sep 17 00:00:00 2001 From: Rachel Lai <83943128+reiyi-lai@users.noreply.github.com> Date: Sat, 17 Jan 2026 23:49:03 -0500 Subject: [PATCH 1/2] Add API route documentation with JSONL patch format specification --- README.md | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0594c67c..3c60e8fa 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,68 @@ const registry = { }; ``` -### 3. Let AI Generate +### 3. Create Your API Route (backend) +`useUIStream` expects JSONL patches from your API in this exact format: +```jsonl +{"op":"set","path":"/root","value":"element-key"} +{"op":"set","path":"/elements/{key}","value":{"key":"...","type":"...","props":{...},"children":[...]}} +``` + +#### useUIStream Reference +```typescript +const { tree, isStreaming, error, send, clear } = useUIStream({ + api: "/api/generate", + onComplete: (tree) => {}, + onError: (error) => {}, +}); + +// send() takes a STRING, not an object +await send("Create a dashboard"); +``` + +#### Example API Route (Next.js + AI SDK) + +```typescript +// app/api/generate/route.ts +import { openai } from "@ai-sdk/openai"; +import { streamText } from "ai"; + +const SYSTEM_PROMPT = `Output JSONL patches to build UI. Format: +{"op":"set","path":"/root","value":"key"} +{"op":"set","path":"/elements/key","value":{"key":"...","type":"...","props":{...},"children":[...]}} + +Rules: One JSON per line. No markdown. children is array of key strings.`; + +export async function POST(req: Request) { + const { prompt } = await req.json(); + const result = streamText({ + model: openai("gpt-4o"), + system: SYSTEM_PROMPT, + prompt, + }); + return result.toTextStreamResponse(); +} +``` + +#### Rendering the Tree + +The `Renderer` component requires context providers – use `JSONUIProvider`: + +```tsx +import { Renderer, JSONUIProvider } from "@json-render/react"; + +function App() { + const { tree, isStreaming } = useUIStream({ api: "/api/generate" }); + + return ( + + {tree && } + + ); +} +``` + +### 4. Let AI Generate ```tsx import { DataProvider, ActionProvider, Renderer, useUIStream } from '@json-render/react'; From 6c45a3259e2b76f89ab79a622491b963c14d8a8b Mon Sep 17 00:00:00 2001 From: Rachel Lai <83943128+reiyi-lai@users.noreply.github.com> Date: Sun, 18 Jan 2026 00:22:04 -0500 Subject: [PATCH 2/2] fix(docs): correct JSONL patch format to use /elements/{key} paths --- README.md | 2 +- apps/web/app/docs/streaming/page.tsx | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 3c60e8fa..4b3a45a7 100644 --- a/README.md +++ b/README.md @@ -152,7 +152,7 @@ function Dashboard() { placeholder="Create a revenue dashboard..." onKeyDown={(e) => e.key === 'Enter' && send(e.target.value)} /> - + ); diff --git a/apps/web/app/docs/streaming/page.tsx b/apps/web/app/docs/streaming/page.tsx index 29d83204..c33bec80 100644 --- a/apps/web/app/docs/streaming/page.tsx +++ b/apps/web/app/docs/streaming/page.tsx @@ -17,9 +17,10 @@ export default function StreamingPage() { json-render uses JSONL (JSON Lines) streaming. As AI generates, each line represents a patch operation:

- {`{"op":"set","path":"/root","value":{"key":"root","type":"Card","props":{"title":"Dashboard"}}} -{"op":"add","path":"/root/children","value":{"key":"metric-1","type":"Metric","props":{"label":"Revenue"}}} -{"op":"add","path":"/root/children","value":{"key":"metric-2","type":"Metric","props":{"label":"Users"}}}`} + {`{"op":"set","path":"/root","value":"dashboard"} +{"op":"set","path":"/elements/dashboard","value":{"key":"dashboard","type":"Card","props":{"title":"Dashboard"},"children":["metric-1","metric-2"]}} +{"op":"set","path":"/elements/metric-1","value":{"key":"metric-1","type":"Metric","props":{"label":"Revenue"}}} +{"op":"set","path":"/elements/metric-2","value":{"key":"metric-2","type":"Metric","props":{"label":"Users"}}}`}

useUIStream Hook