page.tsx 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. import Link from "next/link";
  2. import { Button } from "@/components/ui/button";
  3. import { Code } from "@/components/code";
  4. export const metadata = {
  5. title: "Quick Start | json-render",
  6. };
  7. export default function QuickStartPage() {
  8. return (
  9. <article>
  10. <h1 className="text-3xl font-bold mb-4">Quick Start</h1>
  11. <p className="text-muted-foreground mb-8">
  12. Get up and running with json-render in 5 minutes.
  13. </p>
  14. <h2 className="text-xl font-semibold mt-12 mb-4">1. Define your catalog</h2>
  15. <p className="text-sm text-muted-foreground mb-4">
  16. Create a catalog that defines what components AI can use:
  17. </p>
  18. <Code lang="typescript">{`// lib/catalog.ts
  19. import { createCatalog } from '@json-render/core';
  20. import { z } from 'zod';
  21. export const catalog = createCatalog({
  22. components: {
  23. Card: {
  24. props: z.object({
  25. title: z.string(),
  26. description: z.string().nullable(),
  27. }),
  28. hasChildren: true,
  29. },
  30. Button: {
  31. props: z.object({
  32. label: z.string(),
  33. action: z.string(),
  34. }),
  35. },
  36. Text: {
  37. props: z.object({
  38. content: z.string(),
  39. }),
  40. },
  41. },
  42. actions: {
  43. submit: {
  44. params: z.object({ formId: z.string() }),
  45. },
  46. navigate: {
  47. params: z.object({ url: z.string() }),
  48. },
  49. },
  50. });`}</Code>
  51. <h2 className="text-xl font-semibold mt-12 mb-4">2. Create your components</h2>
  52. <p className="text-sm text-muted-foreground mb-4">
  53. Register React components that render each catalog type:
  54. </p>
  55. <Code lang="tsx">{`// components/registry.tsx
  56. export const registry = {
  57. Card: ({ element, children }) => (
  58. <div className="p-4 border rounded-lg">
  59. <h2 className="font-bold">{element.props.title}</h2>
  60. {element.props.description && (
  61. <p className="text-gray-600">{element.props.description}</p>
  62. )}
  63. {children}
  64. </div>
  65. ),
  66. Button: ({ element, onAction }) => (
  67. <button
  68. className="px-4 py-2 bg-blue-500 text-white rounded"
  69. onClick={() => onAction(element.props.action, {})}
  70. >
  71. {element.props.label}
  72. </button>
  73. ),
  74. Text: ({ element }) => (
  75. <p>{element.props.content}</p>
  76. ),
  77. };`}</Code>
  78. <h2 className="text-xl font-semibold mt-12 mb-4">3. Create an API route</h2>
  79. <p className="text-sm text-muted-foreground mb-4">
  80. Set up a streaming API route for AI generation:
  81. </p>
  82. <Code lang="typescript">{`// app/api/generate/route.ts
  83. import { streamText } from 'ai';
  84. import { openai } from '@ai-sdk/openai';
  85. import { generateCatalogPrompt } from '@json-render/core';
  86. import { catalog } from '@/lib/catalog';
  87. export async function POST(req: Request) {
  88. const { prompt } = await req.json();
  89. const systemPrompt = generateCatalogPrompt(catalog);
  90. const result = streamText({
  91. model: openai('gpt-4o'),
  92. system: systemPrompt,
  93. prompt,
  94. });
  95. return new Response(result.textStream, {
  96. headers: { 'Content-Type': 'text/plain; charset=utf-8' },
  97. });
  98. }`}</Code>
  99. <h2 className="text-xl font-semibold mt-12 mb-4">4. Render the UI</h2>
  100. <p className="text-sm text-muted-foreground mb-4">
  101. Use the providers and renderer to display AI-generated UI:
  102. </p>
  103. <Code lang="tsx">{`// app/page.tsx
  104. 'use client';
  105. import { DataProvider, ActionProvider, VisibilityProvider, Renderer, useUIStream } from '@json-render/react';
  106. import { registry } from '@/components/registry';
  107. export default function Page() {
  108. const { tree, isLoading, generate } = useUIStream({
  109. endpoint: '/api/generate',
  110. });
  111. const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
  112. e.preventDefault();
  113. const formData = new FormData(e.currentTarget);
  114. generate(formData.get('prompt') as string);
  115. };
  116. return (
  117. <DataProvider initialData={{}}>
  118. <VisibilityProvider>
  119. <ActionProvider handlers={{
  120. submit: (params) => console.log('Submit:', params),
  121. navigate: (params) => console.log('Navigate:', params),
  122. }}>
  123. <form onSubmit={handleSubmit}>
  124. <input
  125. name="prompt"
  126. placeholder="Describe what you want..."
  127. className="border p-2 rounded"
  128. />
  129. <button type="submit" disabled={isLoading}>
  130. Generate
  131. </button>
  132. </form>
  133. <div className="mt-8">
  134. <Renderer tree={tree} registry={registry} />
  135. </div>
  136. </ActionProvider>
  137. </VisibilityProvider>
  138. </DataProvider>
  139. );
  140. }`}</Code>
  141. <h2 className="text-xl font-semibold mt-12 mb-4">Next steps</h2>
  142. <ul className="list-disc list-inside text-sm text-muted-foreground space-y-2">
  143. <li>Learn about <Link href="/docs/catalog" className="text-foreground hover:underline">catalogs</Link> in depth</li>
  144. <li>Explore <Link href="/docs/data-binding" className="text-foreground hover:underline">data binding</Link> for dynamic values</li>
  145. <li>Add <Link href="/docs/actions" className="text-foreground hover:underline">actions</Link> for interactivity</li>
  146. <li>Implement <Link href="/docs/visibility" className="text-foreground hover:underline">conditional visibility</Link></li>
  147. </ul>
  148. <div className="flex gap-3 mt-12">
  149. <Button size="sm" asChild>
  150. <Link href="/docs/catalog">Learn about Catalogs</Link>
  151. </Button>
  152. </div>
  153. </article>
  154. );
  155. }