| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239 |
- import { pageMetadata } from "@/lib/page-metadata"
- export const metadata = pageMetadata("docs/ai-sdk")
- # AI SDK Integration
- Use json-render with the [Vercel AI SDK](https://sdk.vercel.ai) for seamless streaming. json-render supports two modes: **Generate** (standalone UI) and **Chat** (UI embedded in conversation). See [Generation Modes](/docs/generation-modes) for a detailed comparison.
- ## Installation
- ```bash
- npm install ai @ai-sdk/react
- ```
- ## Generate Mode
- In generate mode, the AI outputs only JSONL patches. The entire response is a UI spec with no prose. This is the default mode and is ideal for playgrounds, builders, and dashboard generators.
- ### API Route
- ```typescript
- // app/api/generate/route.ts
- import { streamText } from "ai";
- import { catalog } from "@/lib/catalog";
- export async function POST(req: Request) {
- const { prompt, currentTree } = await req.json();
- const systemPrompt = catalog.prompt();
- // Optionally include current UI state for context
- const contextPrompt = currentTree
- ? `\n\nCurrent UI state:\n${JSON.stringify(currentTree, null, 2)}`
- : "";
- const result = streamText({
- model: yourModel,
- system: systemPrompt + contextPrompt,
- prompt,
- });
- return result.toTextStreamResponse();
- }
- ```
- ### Client
- Use `useUIStream` on the client to compile the JSONL stream into a spec:
- ```tsx
- "use client";
- import { useUIStream, Renderer } from "@json-render/react";
- function GenerativeUI() {
- const { spec, isStreaming, error, send } = useUIStream({
- api: "/api/generate",
- });
- return (
- <div>
- <button
- onClick={() => send("Create a dashboard with metrics")}
- disabled={isStreaming}
- >
- {isStreaming ? "Generating..." : "Generate"}
- </button>
- {error && <p className="text-red-500">{error.message}</p>}
- <Renderer spec={spec} registry={registry} loading={isStreaming} />
- </div>
- );
- }
- ```
- ## Chat Mode
- In chat mode, the AI responds conversationally and includes JSONL patches inline. Text-only replies are allowed when no UI is needed. This is ideal for chatbots, copilots, and educational assistants.
- ### API Route
- Use `pipeJsonRender` to separate text from JSONL patches in the stream. Patches are emitted as data parts that the client can pick up.
- ```typescript
- // app/api/chat/route.ts
- import { streamText } from "ai";
- import { pipeJsonRender } from "@json-render/core";
- import {
- createUIMessageStream,
- createUIMessageStreamResponse,
- } from "ai";
- import { catalog } from "@/lib/catalog";
- export async function POST(req: Request) {
- const { messages } = await req.json();
- const result = streamText({
- model: yourModel,
- system: catalog.prompt({ mode: "chat" }),
- messages,
- });
- const stream = createUIMessageStream({
- execute: async ({ writer }) => {
- writer.merge(pipeJsonRender(result.toUIMessageStream()));
- },
- });
- return createUIMessageStreamResponse({ stream });
- }
- ```
- ### Client
- Use `useChat` from the AI SDK and `useJsonRenderMessage` from json-render to extract the spec from each message:
- ```tsx
- "use client";
- import { useChat } from "@ai-sdk/react";
- import { useJsonRenderMessage, Renderer } from "@json-render/react";
- function Chat() {
- const { messages, input, handleInputChange, handleSubmit } = useChat({
- api: "/api/chat",
- });
- return (
- <div>
- <div>
- {messages.map((msg) => (
- <ChatMessage key={msg.id} message={msg} />
- ))}
- </div>
- <form onSubmit={handleSubmit}>
- <input
- value={input}
- onChange={handleInputChange}
- placeholder="Ask something..."
- />
- <button type="submit">Send</button>
- </form>
- </div>
- );
- }
- function ChatMessage({ message }: { message: { parts: Array<{ type: string; text?: string; data?: unknown }> } }) {
- const { spec, text, hasSpec } = useJsonRenderMessage(message.parts);
- return (
- <div>
- {text && <p>{text}</p>}
- {hasSpec && spec && (
- <Renderer spec={spec} registry={registry} />
- )}
- </div>
- );
- }
- ```
- ## Prompt Engineering
- The `catalog.prompt()` method creates an optimized system prompt that:
- - Lists all available components and their props
- - Describes available actions
- - Specifies the expected output format (JSONL-only or text + JSONL depending on mode)
- - Includes examples for better generation
- ### Custom Rules
- Pass custom rules to tailor AI behavior:
- ```typescript
- const systemPrompt = catalog.prompt({
- customRules: [
- "Always use Card components for grouping related content",
- "Prefer horizontal layouts (Row) for metrics",
- "Use consistent spacing with padding=\"md\"",
- ],
- });
- ```
- ### Chat Mode Prompt
- ```typescript
- const chatPrompt = catalog.prompt({ mode: "chat" });
- ```
- In chat mode, the prompt instructs the AI to respond conversationally first, then include JSONL patches on their own lines when UI is needed. Text-only replies are allowed.
- ## Which Mode?
- <div className="my-6 overflow-x-auto">
- <table className="mdx-table w-full text-sm border-collapse">
- <thead>
- <tr>
- <th></th>
- <th>Generate</th>
- <th>Chat</th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td>Output</td>
- <td>JSONL only</td>
- <td>Text + JSONL</td>
- </tr>
- <tr>
- <td>Text-only replies</td>
- <td>No</td>
- <td>Yes</td>
- </tr>
- <tr>
- <td>System prompt</td>
- <td><code>catalog.prompt()</code></td>
- <td><code>{"catalog.prompt({ mode: \"chat\" })"}</code></td>
- </tr>
- <tr>
- <td>Stream utility</td>
- <td><code>useUIStream</code></td>
- <td><code>pipeJsonRender</code> + <code>useJsonRenderMessage</code></td>
- </tr>
- <tr>
- <td>Use case</td>
- <td>Playgrounds, builders</td>
- <td>Chatbots, copilots</td>
- </tr>
- </tbody>
- </table>
- </div>
- Learn more in the [Generation Modes](/docs/generation-modes) guide.
- ## Next
- - Learn about [progressive streaming](/docs/streaming)
- - See the [chat example](https://github.com/vercel-labs/json-render/tree/main/examples/chat) for a complete implementation
|