| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220 |
- import Link from "next/link";
- import { Code } from "@/components/code";
- export const metadata = {
- title: "Quick Start | json-render",
- };
- export default function QuickStartPage() {
- return (
- <article>
- <h1 className="text-3xl font-bold mb-4">Quick Start</h1>
- <p className="text-muted-foreground mb-8">
- Get up and running with json-render in 5 minutes.
- </p>
- <h2 className="text-xl font-semibold mt-12 mb-4">
- 1. Define your catalog
- </h2>
- <p className="text-sm text-muted-foreground mb-4">
- Create a catalog that defines what components AI can use:
- </p>
- <Code lang="typescript">{`// lib/catalog.ts
- import { defineCatalog } from '@json-render/core';
- import { schema } from '@json-render/react';
- import { z } from 'zod';
- export const catalog = defineCatalog(schema, {
- components: {
- Card: {
- props: z.object({
- title: z.string(),
- description: z.string().nullable(),
- }),
- slots: ["default"],
- description: "Container card with optional title",
- },
- Button: {
- props: z.object({
- label: z.string(),
- action: z.string().nullable(),
- }),
- description: "Clickable button that triggers an action",
- },
- Text: {
- props: z.object({
- content: z.string(),
- }),
- description: "Text paragraph",
- },
- },
- actions: {
- submit: {
- params: z.object({ formId: z.string() }),
- description: "Submit a form",
- },
- navigate: {
- params: z.object({ url: z.string() }),
- description: "Navigate to a URL",
- },
- },
- });`}</Code>
- <h2 className="text-xl font-semibold mt-12 mb-4">
- 2. Define your components
- </h2>
- <p className="text-sm text-muted-foreground mb-4">
- Use <code className="text-foreground">defineRegistry</code> to map
- catalog types to React components. Each component receives type-safe{" "}
- <code className="text-foreground">props</code>,{" "}
- <code className="text-foreground">children</code>, and{" "}
- <code className="text-foreground">onAction</code>:
- </p>
- <Code lang="tsx">{`// lib/registry.tsx
- import { defineRegistry } from '@json-render/react';
- import { catalog } from './catalog';
- export const { registry } = defineRegistry(catalog, {
- components: {
- Card: ({ props, children }) => (
- <div className="p-4 border rounded-lg">
- <h2 className="font-bold">{props.title}</h2>
- {props.description && (
- <p className="text-gray-600">{props.description}</p>
- )}
- {children}
- </div>
- ),
- Button: ({ props, onAction }) => (
- <button
- className="px-4 py-2 bg-blue-500 text-white rounded"
- onClick={() => onAction?.({ name: props.action })}
- >
- {props.label}
- </button>
- ),
- Text: ({ props }) => (
- <p>{props.content}</p>
- ),
- },
- });`}</Code>
- <h2 className="text-xl font-semibold mt-12 mb-4">
- 3. Create an API route
- </h2>
- <p className="text-sm text-muted-foreground mb-4">
- Set up a streaming API route for AI generation:
- </p>
- <Code lang="typescript">{`// app/api/generate/route.ts
- import { streamText } from 'ai';
- import { catalog } from '@/lib/catalog';
- export async function POST(req: Request) {
- const { prompt } = await req.json();
- // Generate system prompt from catalog
- const systemPrompt = catalog.prompt();
- const result = streamText({
- model: 'anthropic/claude-haiku-4.5',
- system: systemPrompt,
- prompt,
- });
- return result.toTextStreamResponse();
- }`}</Code>
- <h2 className="text-xl font-semibold mt-12 mb-4">4. Render the UI</h2>
- <p className="text-sm text-muted-foreground mb-4">
- Use providers and the <code className="text-foreground">Renderer</code>{" "}
- with your registry to display AI-generated UI:
- </p>
- <Code lang="tsx">{`// app/page.tsx
- 'use client';
- import { Renderer, DataProvider, ActionProvider, VisibilityProvider, useUIStream } from '@json-render/react';
- import { registry } from '@/lib/registry';
- export default function Page() {
- const { spec, isStreaming, send } = useUIStream({
- api: '/api/generate',
- });
- const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
- e.preventDefault();
- const formData = new FormData(e.currentTarget);
- send(formData.get('prompt') as string);
- };
- return (
- <DataProvider initialData={{}}>
- <VisibilityProvider>
- <ActionProvider handlers={{
- submit: (params) => console.log('Submit:', params),
- navigate: (params) => console.log('Navigate:', params),
- }}>
- <form onSubmit={handleSubmit}>
- <input
- name="prompt"
- placeholder="Describe what you want..."
- className="border p-2 rounded"
- />
- <button type="submit" disabled={isStreaming}>
- Generate
- </button>
- </form>
- <div className="mt-8">
- <Renderer spec={spec} registry={registry} loading={isStreaming} />
- </div>
- </ActionProvider>
- </VisibilityProvider>
- </DataProvider>
- );
- }`}</Code>
- <h2 className="text-xl font-semibold mt-12 mb-4">Next steps</h2>
- <ul className="list-disc list-inside text-sm text-muted-foreground space-y-2">
- <li>
- Learn about{" "}
- <Link
- href="/docs/catalog"
- className="text-foreground hover:underline"
- >
- catalogs
- </Link>{" "}
- in depth
- </li>
- <li>
- Explore{" "}
- <Link
- href="/docs/data-binding"
- className="text-foreground hover:underline"
- >
- data binding
- </Link>{" "}
- for dynamic values
- </li>
- <li>
- Add{" "}
- <Link
- href="/docs/actions"
- className="text-foreground hover:underline"
- >
- actions
- </Link>{" "}
- for interactivity
- </li>
- <li>
- Implement{" "}
- <Link
- href="/docs/visibility"
- className="text-foreground hover:underline"
- >
- conditional visibility
- </Link>
- </li>
- </ul>
- </article>
- );
- }
|