Some checks failed
Build and Push Docker Image / build (push) Has been cancelled
229 lines
5.2 KiB
Markdown
229 lines
5.2 KiB
Markdown
# 🎯 How json-render Works in This Demo
|
||
|
||
## The Flow
|
||
|
||
```
|
||
User Prompt (in production)
|
||
↓
|
||
AI generates JSON spec (constrained to catalog)
|
||
↓
|
||
json-render Renderer component
|
||
↓
|
||
Beautiful React UI
|
||
```
|
||
|
||
## Step-by-Step Example
|
||
|
||
### 1. Define Your Component Catalog
|
||
|
||
`lib/catalog.ts`:
|
||
|
||
```typescript
|
||
import { defineCatalog } from "@json-render/core";
|
||
import { schema } from "@json-render/react";
|
||
import { z } from "zod";
|
||
|
||
export const catalog = defineCatalog(schema, {
|
||
components: {
|
||
RecipeHeader: {
|
||
props: z.object({
|
||
title: z.string(),
|
||
chef: z.string(),
|
||
prepTime: z.string(),
|
||
}),
|
||
description: "Header with recipe info",
|
||
},
|
||
IngredientList: {
|
||
props: z.object({
|
||
title: z.string(),
|
||
ingredients: z.array(
|
||
z.object({
|
||
name: z.string(),
|
||
quantity: z.string(),
|
||
})
|
||
),
|
||
}),
|
||
description: "List of ingredients",
|
||
},
|
||
// ... more components
|
||
},
|
||
actions: {},
|
||
});
|
||
```
|
||
|
||
**This is your guardrail**: AI can only use these components.
|
||
|
||
### 2. Map Components to React
|
||
|
||
`lib/registry.tsx`:
|
||
|
||
```typescript
|
||
import { defineRegistry } from "@json-render/react";
|
||
import { catalog } from "./catalog";
|
||
|
||
export const { registry } = defineRegistry(catalog, {
|
||
components: {
|
||
RecipeHeader: ({ props }) => (
|
||
<div className="bg-amber-50 p-8 rounded-2xl">
|
||
<h1 className="text-4xl font-bold">{props.title}</h1>
|
||
<span>👨🍳 Chef: {props.chef}</span>
|
||
<span>⏱️ Prep: {props.prepTime}</span>
|
||
</div>
|
||
),
|
||
IngredientList: ({ props }) => (
|
||
<div className="bg-green-50 p-6">
|
||
<h3>{props.title}</h3>
|
||
<ul>
|
||
{props.ingredients.map((ing, i) => (
|
||
<li key={i}>
|
||
{ing.quantity} {ing.name}
|
||
</li>
|
||
))}
|
||
</ul>
|
||
</div>
|
||
),
|
||
// ... more implementations
|
||
},
|
||
});
|
||
```
|
||
|
||
### 3. Create a JSON Spec
|
||
|
||
`lib/recipes.ts`:
|
||
|
||
```typescript
|
||
const recipe = {
|
||
root: "root", // Start rendering from this element
|
||
elements: {
|
||
root: {
|
||
type: "RecipeCard", // Component from catalog
|
||
props: {
|
||
title: "Crêpes Suzette",
|
||
description: "Classic French dessert"
|
||
},
|
||
children: ["header", "ingredients"] // Refs to other elements
|
||
},
|
||
header: {
|
||
type: "RecipeHeader",
|
||
props: {
|
||
title: "Crêpes Suzette",
|
||
chef: "Auguste Escoffier",
|
||
prepTime: "45 minutes"
|
||
}
|
||
},
|
||
ingredients: {
|
||
type: "IngredientList",
|
||
props: {
|
||
title: "Ingredients",
|
||
ingredients: [
|
||
{ quantity: "200g", name: "Flour" },
|
||
{ quantity: "4", name: "Eggs" },
|
||
{ quantity: "500ml", name: "Milk" }
|
||
]
|
||
}
|
||
}
|
||
}
|
||
};
|
||
```
|
||
|
||
**This is what AI generates** (but here we've pre-written it for the demo).
|
||
|
||
### 4. Render with json-render
|
||
|
||
`app/page.tsx`:
|
||
|
||
```typescript
|
||
import { Renderer } from "@json-render/react";
|
||
import { registry, Provider } from "@/lib/registry";
|
||
import { recipes } from "@/lib/recipes";
|
||
|
||
export default function Home() {
|
||
return (
|
||
<main>
|
||
{recipes.map((recipe, index) => (
|
||
<Provider key={index}>
|
||
<Renderer spec={recipe} registry={registry} />
|
||
</Provider>
|
||
))}
|
||
</main>
|
||
);
|
||
}
|
||
```
|
||
|
||
**Magic happens here**: The `Renderer` takes your spec + registry and outputs fully-styled React components!
|
||
|
||
## What Gets Rendered
|
||
|
||
The spec above becomes:
|
||
|
||
```jsx
|
||
<div className="bg-white p-8 rounded-2xl shadow-xl">
|
||
<h2>Crêpes Suzette</h2>
|
||
<p>Classic French dessert</p>
|
||
|
||
<div className="bg-amber-50 p-8 rounded-2xl">
|
||
<h1 className="text-4xl font-bold">Crêpes Suzette</h1>
|
||
<span>👨🍳 Chef: Auguste Escoffier</span>
|
||
<span>⏱️ Prep: 45 minutes</span>
|
||
</div>
|
||
|
||
<div className="bg-green-50 p-6">
|
||
<h3>Ingredients</h3>
|
||
<ul>
|
||
<li>200g Flour</li>
|
||
<li>4 Eggs</li>
|
||
<li>500ml Milk</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
```
|
||
|
||
## Why This Matters
|
||
|
||
### Without json-render:
|
||
- AI generates raw HTML/JSX → **Security risk** (XSS, code injection)
|
||
- No type safety → **Runtime errors**
|
||
- Unpredictable output → **Broken UIs**
|
||
|
||
### With json-render:
|
||
- ✅ AI can only use predefined components (guardrailed)
|
||
- ✅ Zod schemas enforce type safety (predictable)
|
||
- ✅ Your React components control the rendering (safe)
|
||
- ✅ Stream rendering as AI generates (fast)
|
||
|
||
## In Production with AI
|
||
|
||
```typescript
|
||
// User types prompt
|
||
const userPrompt = "Show me a recipe for chocolate chip cookies";
|
||
|
||
// AI generates spec (using your catalog as context)
|
||
const aiGeneratedSpec = await ai.generate({
|
||
prompt: userPrompt,
|
||
catalog: catalog.prompt(), // Gives AI the component vocabulary
|
||
});
|
||
|
||
// Render safely
|
||
<Renderer spec={aiGeneratedSpec} registry={registry} />
|
||
```
|
||
|
||
**Result**: AI-generated UIs that are:
|
||
1. **Safe** - Can't inject malicious code
|
||
2. **Type-safe** - Matches your schemas
|
||
3. **Beautiful** - Uses your styled components
|
||
4. **Fast** - Streams as AI generates
|
||
|
||
## The Power
|
||
|
||
You define the components once. Then:
|
||
- AI can generate infinite variations
|
||
- Users can describe UIs in natural language
|
||
- Your app stays safe and consistent
|
||
- No manual UI coding needed
|
||
|
||
**That's json-render.**
|
||
|
||
---
|
||
|
||
Check the full code in `lib/` to see it in action! 🚀
|