page.tsx 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. import { Code } from "@/components/code";
  2. export const metadata = {
  3. title: "Streaming | json-render",
  4. };
  5. export default function StreamingPage() {
  6. return (
  7. <article>
  8. <h1 className="text-3xl font-bold mb-4">Streaming</h1>
  9. <p className="text-muted-foreground mb-8">
  10. Progressively render UI as AI generates it.
  11. </p>
  12. <h2 className="text-xl font-semibold mt-12 mb-4">How Streaming Works</h2>
  13. <p className="text-sm text-muted-foreground mb-4">
  14. json-render uses JSONL (JSON Lines) streaming. As AI generates, each
  15. line represents a patch operation:
  16. </p>
  17. <Code lang="json">{`{"op":"set","path":"/root","value":{"key":"root","type":"Card","props":{"title":"Dashboard"}}}
  18. {"op":"add","path":"/root/children","value":{"key":"metric-1","type":"Metric","props":{"label":"Revenue"}}}
  19. {"op":"add","path":"/root/children","value":{"key":"metric-2","type":"Metric","props":{"label":"Users"}}}`}</Code>
  20. <h2 className="text-xl font-semibold mt-12 mb-4">useUIStream Hook</h2>
  21. <p className="text-sm text-muted-foreground mb-4">
  22. The hook handles parsing and state management:
  23. </p>
  24. <Code lang="tsx">{`import { useUIStream } from '@json-render/react';
  25. function App() {
  26. const {
  27. tree, // Current UI tree state
  28. isLoading, // True while streaming
  29. error, // Any error that occurred
  30. generate, // Function to start generation
  31. abort, // Function to cancel streaming
  32. } = useUIStream({
  33. endpoint: '/api/generate',
  34. });
  35. }`}</Code>
  36. <h2 className="text-xl font-semibold mt-12 mb-4">Patch Operations</h2>
  37. <p className="text-sm text-muted-foreground mb-4">
  38. Supported operations:
  39. </p>
  40. <ul className="list-disc list-inside text-sm text-muted-foreground space-y-2 mb-4">
  41. <li>
  42. <code className="text-foreground">set</code> — Set the value at a path
  43. (creates if needed)
  44. </li>
  45. <li>
  46. <code className="text-foreground">add</code> — Add to an array at a
  47. path
  48. </li>
  49. <li>
  50. <code className="text-foreground">replace</code> — Replace value at a
  51. path
  52. </li>
  53. <li>
  54. <code className="text-foreground">remove</code> — Remove value at a
  55. path
  56. </li>
  57. </ul>
  58. <h2 className="text-xl font-semibold mt-12 mb-4">Path Format</h2>
  59. <p className="text-sm text-muted-foreground mb-4">
  60. Paths use a key-based format for elements:
  61. </p>
  62. <Code lang="bash">{`/root -> Root element
  63. /root/children -> Children of root
  64. /elements/card-1 -> Element with key "card-1"
  65. /elements/card-1/children -> Children of card-1`}</Code>
  66. <h2 className="text-xl font-semibold mt-12 mb-4">Server-Side Setup</h2>
  67. <p className="text-sm text-muted-foreground mb-4">
  68. Ensure your API route streams properly:
  69. </p>
  70. <Code lang="typescript">{`export async function POST(req: Request) {
  71. const { prompt } = await req.json();
  72. const result = streamText({
  73. model: 'anthropic/claude-opus-4.5',
  74. system: generateCatalogPrompt(catalog),
  75. prompt,
  76. });
  77. // Return as a streaming response
  78. return new Response(result.textStream, {
  79. headers: {
  80. 'Content-Type': 'text/plain; charset=utf-8',
  81. 'Transfer-Encoding': 'chunked',
  82. 'Cache-Control': 'no-cache',
  83. },
  84. });
  85. }`}</Code>
  86. <h2 className="text-xl font-semibold mt-12 mb-4">
  87. Progressive Rendering
  88. </h2>
  89. <p className="text-sm text-muted-foreground mb-4">
  90. The Renderer automatically updates as the tree changes:
  91. </p>
  92. <Code lang="tsx">{`function App() {
  93. const { tree, isLoading } = useUIStream({ endpoint: '/api/generate' });
  94. return (
  95. <div>
  96. {isLoading && <LoadingIndicator />}
  97. <Renderer tree={tree} registry={registry} />
  98. </div>
  99. );
  100. }`}</Code>
  101. <h2 className="text-xl font-semibold mt-12 mb-4">Aborting Streams</h2>
  102. <Code lang="tsx">{`function App() {
  103. const { isLoading, generate, abort } = useUIStream({
  104. endpoint: '/api/generate',
  105. });
  106. return (
  107. <div>
  108. <button onClick={() => generate('Create dashboard')}>
  109. Generate
  110. </button>
  111. {isLoading && (
  112. <button onClick={abort}>Cancel</button>
  113. )}
  114. </div>
  115. );
  116. }`}</Code>
  117. </article>
  118. );
  119. }