page.tsx 5.8 KB

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