Initial commit: json-render crepes demo
Some checks failed
Build and Push Docker Image / build (push) Has been cancelled
Some checks failed
Build and Push Docker Image / build (push) Has been cancelled
This commit is contained in:
228
HOW_IT_WORKS.md
Normal file
228
HOW_IT_WORKS.md
Normal file
@@ -0,0 +1,228 @@
|
||||
# 🎯 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! 🚀
|
||||
Reference in New Issue
Block a user