Skip to content

Documentation mismatch: useUIStream expects JSONL patch format but API route example outputs plain JSON #42

@vkboo

Description

@vkboo

Problem

The documentation for API route setup in AI SDK Integration and Streaming shows a simple example that directly returns result.textStream:

// From docs (https://json-render.dev/docs/ai-sdk)
export async function POST(req: Request) {
  const { prompt } = await req.json();
  const systemPrompt = generateCatalogPrompt(catalog);

  const result = streamText({
    model: 'anthropic/claude-haiku-4.5',
    system: systemPrompt,
    prompt,
  });

  return new Response(result.textStream, {
    headers: { 'Content-Type': 'text/plain; charset=utf-8' },
  });
}

I followed this pattern with a minor difference (using @ai-sdk/openai provider instead of AI Gateway string format):

// My implementation
import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';
import { generateCatalogPrompt } from '@json-render/core';
import { catalog } from '@/lib/catalog';

export async function POST(req: Request) {
  const { prompt } = await req.json();
  const systemPrompt = generateCatalogPrompt(catalog);

  const result = streamText({
    model: openai('gpt-5.2'),  // Using @ai-sdk/openai provider
    system: systemPrompt,
    prompt,
  });

  return new Response(result.textStream, {
    headers: { 'Content-Type': 'text/plain; charset=utf-8' },
  });
}

Actual Output

The AI model outputs nested JSON (not JSONL patch format):

{
  "type": "Card",
  "props": {
    "title": "Claim Form"
  },
  "children": [
    {
      "type": "Text",
      "props": {
        "text": "Please review your details and submit your claim."
      }
    },
    {
      "type": "Button",
      "props": {
        "text": "Submit Claim",
        "action": {
          "type": "submit"
        }
      }
    }
  ]
}

Expected Format by useUIStream

However, looking at the source code of useUIStream, it expects JSONL patch format (one JSON patch per line):

{"op":"set","path":"/root","value":"el-1"}
{"op":"add","path":"/elements/el-1","value":{"key":"el-1","type":"Card","props":{...}}}
{"op":"add","path":"/elements/el-2","value":{"key":"el-2","type":"Text","props":{...},"parentKey":"el-1"}}

The hook parses each line as a patch operation:

// From @json-render/react source
const lines = buffer.split("\n");
for (const line of lines) {
  const patch = parsePatchLine(line);
  if (patch) {
    currentTree = applyPatch(currentTree, patch);
  }
}

Result

When following the documentation example:

  1. AI model outputs nested JSON (as shown above)
  2. useUIStream tries to parse each line as a JSON patch
  3. Parsing fails because the format doesn't match
  4. tree remains { root: "", elements: {} } (empty)
  5. Nothing renders on the page

Environment

  • @json-render/core: ^0.3.0
  • @json-render/react: ^0.2.0
  • ai: ^6.0.44
  • @ai-sdk/openai: latest

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions