diff --git a/README.md b/README.md index 0594c67..4b3a45a 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'; @@ -91,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 29d8320..c33bec8 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