page.tsx 6.2 KB

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