import { Code } from "@/components/code"; export const metadata = { title: "Streaming | json-render", }; export default function StreamingPage() { return (

Streaming

Progressively render UI as AI generates it.

How Streaming Works

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"}}}`}

useUIStream Hook

The hook handles parsing and state management:

{`import { useUIStream } from '@json-render/react'; function App() { const { tree, // Current UI tree state isLoading, // True while streaming error, // Any error that occurred generate, // Function to start generation abort, // Function to cancel streaming } = useUIStream({ endpoint: '/api/generate', }); }`}

Patch Operations

Supported operations:

Path Format

Paths use a key-based format for elements:

{`/root -> Root element /root/children -> Children of root /elements/card-1 -> Element with key "card-1" /elements/card-1/children -> Children of card-1`}

Server-Side Setup

Ensure your API route streams properly:

{`export async function POST(req: Request) { const { prompt } = await req.json(); const result = streamText({ model: openai('gpt-4o'), system: generateCatalogPrompt(catalog), prompt, }); // Return as a streaming response return new Response(result.textStream, { headers: { 'Content-Type': 'text/plain; charset=utf-8', 'Transfer-Encoding': 'chunked', 'Cache-Control': 'no-cache', }, }); }`}

Progressive Rendering

The Renderer automatically updates as the tree changes:

{`function App() { const { tree, isLoading } = useUIStream({ endpoint: '/api/generate' }); return (
{isLoading && }
); }`}

Aborting Streams

{`function App() { const { isLoading, generate, abort } = useUIStream({ endpoint: '/api/generate', }); return (
{isLoading && ( )}
); }`}
); }