PettyUI/.firecrawl/json-render.md
Mats Bosson db906fd85a Fix linting config and package fields
- Replace .eslintrc.cjs with eslint.config.mjs (ESLint 9 flat config)
  using direct eslint-plugin-solid + @typescript-eslint/parser approach
- Add @typescript-eslint/parser to root devDependencies
- Add main/module/types top-level fields to packages/core/package.json
- Add resolve.conditions to packages/core/vite.config.ts
- Create packages/core/tsconfig.test.json for test type-checking
- Remove empty paths:{} from packages/core/tsconfig.json
2026-03-29 02:35:57 +07:00

597 lines
11 KiB
Markdown

The Generative UI Framework
# AI → json-render → UI
Generate dynamic, personalized UIs from prompts without sacrificing reliability. Predefined components and actions for safe, predictable output.
Create a contact form with name, email, and message
Create a login form with email and passwordBuild a feedback form with rating stars
jsonnestedstreamcatalog
```
{"op":"add","path":"/root","value":"card"}
{"op":"add","path":"/elements/name","value":{"type":"Input","props":{"label":"Name","name":"name","statePath":"/form/name","checks":[{"type":"required","message":"Name is required"}]}}}
{"op":"add","path":"/elements/email","value":{"type":"Input","props":{"label":"Email","name":"email","type":"email","statePath":"/form/email","checks":[{"type":"required","message":"Email is required"},{"type":"email","message":"Please enter a valid email"}]}}}
```
```
{
"root": "card",
"state": {
"form": {
"name": "",
"email": "",
"message": ""
}
},
"elements": {
"card": {
"type": "Card",
"props": {
"title": "Contact Us",
"maxWidth": "md"
},
"children": [\
"name",\
"email"\
]
},
"name": {
"type": "Input",
"props": {
"label": "Name",
"name": "name",
"statePath": "/form/name",
"checks": [\
{\
"type": "required",\
"message": "Name is required"\
}\
]
}
},
"email": {
"type": "Input",
"props": {
"label": "Email",
"name": "email",
"type": "email",
"statePath": "/form/email",
"checks": [\
{\
"type": "required",\
"message": "Email is required"\
},\
{\
"type": "email",\
"message": "Please enter a valid email"\
}\
]
}
}
}
}
```
```
{
"state": {
"form": {
"name": "",
"email": "",
"message": ""
}
},
"elements": {
"type": "Card",
"props": {
"title": "Contact Us",
"maxWidth": "md"
},
"children": [\
{\
"type": "Input",\
"props": {\
"label": "Name",\
"name": "name",\
"statePath": "/form/name",\
"checks": [\
{\
"type": "required",\
"message": "Name is required"\
}\
]\
}\
},\
{\
"type": "Input",\
"props": {\
"label": "Email",\
"name": "email",\
"type": "email",\
"statePath": "/form/email",\
"checks": [\
{\
"type": "required",\
"message": "Email is required"\
},\
{\
"type": "email",\
"message": "Please enter a valid email"\
}\
]\
}\
}\
]
}
}
```
components (39)actions (6)
Accordion
Collapsible sections. Items as \[{title, content}\]. Type 'single' (default) or 'multiple'.
items: arraytype: enum?
Alert
Alert banner
title: stringmessage: string?type: enum?
Avatar
User avatar with fallback initials
src: string?name: stringsize: enum?
Badge
Status badge
text: stringvariant: enum?
BarGraph
Vertical bar chart
title: string?data: array
Button
Clickable button. Bind on.press for handler.
label: stringvariant: enum?disabled: boolean?
on.press
ButtonGroup
Segmented button group. Use { $bindState } on selected for selected value.
buttons: arrayselected: string?
on.change
Cardslots: default
Container card for content sections. Use for forms/content boxes, NOT for page headers.
title: string?description: string?maxWidth: enum?centered: boolean?
Carousel
Horizontally scrollable carousel of cards.
items: array
Checkbox
Checkbox input. Use { $bindState } on checked for binding.
label: stringname: stringchecked: boolean?
on.change
Collapsibleslots: default
Collapsible section with trigger. Children render inside.
title: stringdefaultOpen: boolean?
Dialogslots: default
Modal dialog. Set openPath to a boolean state path. Use setState to toggle.
title: stringdescription: string?openPath: string
Drawerslots: default
Bottom sheet drawer. Set openPath to a boolean state path. Use setState to toggle.
title: stringdescription: string?openPath: string
DropdownMenu
Dropdown menu with trigger button and selectable items.
label: stringitems: array
on.select
Gridslots: default
Grid layout (1-6 columns)
columns: number?gap: enum?
Heading
Heading text (h1-h4)
text: stringlevel: enum?
Image
Placeholder image (displays alt text in a styled box)
alt: stringwidth: number?height: number?
Input
Text input field. Use { $bindState } on value for two-way binding. Use checks for validation (e.g. required, email, minLength).
label: stringname: stringtype: enum?placeholder: string?value: string?checks: array?
on.submiton.focuson.blur
LineGraph
Line chart with points
title: string?data: array
Link
Anchor link. Bind on.press for click handler.
label: stringhref: string
on.press
Pagination
Page navigation. Use { $bindState } on page for current page number.
totalPages: numberpage: number?
on.change
Popover
Popover that appears on click of trigger.
trigger: stringcontent: string
Progress
Progress bar (value 0-100)
value: numbermax: number?label: string?
Radio
Radio button group. Use { $bindState } on value for binding.
label: stringname: stringoptions: arrayvalue: string?
on.change
Rating
Star rating display
value: numbermax: number?label: string?
Select
Dropdown select input. Use { $bindState } on value for binding. Use checks for validation.
label: stringname: stringoptions: arrayplaceholder: string?value: string?checks: array?
on.change
Separator
Visual separator line
orientation: enum?
Skeleton
Loading placeholder skeleton
width: string?height: string?rounded: boolean?
Slider
Range slider input. Use { $bindState } on value for binding.
label: string?min: number?max: number?step: number?value: number?
on.change
Spinner
Loading spinner indicator
size: enum?label: string?
Stackslots: default
Flex container for layouts
direction: enum?gap: enum?align: enum?justify: enum?
Switch
Toggle switch. Use { $bindState } on checked for binding.
label: stringname: stringchecked: boolean?
on.change
Table
Data table. columns: header labels. rows: 2D array of cell strings, e.g. \[\["Alice","admin"\],\["Bob","user"\]\].
columns: arrayrows: arraycaption: string?
Tabs
Tab navigation. Use { $bindState } on value for active tab binding.
tabs: arraydefaultValue: string?value: string?
on.change
Text
Paragraph text
text: stringvariant: enum?
Textarea
Multi-line text input. Use { $bindState } on value for binding. Use checks for validation.
label: stringname: stringplaceholder: string?rows: number?value: string?checks: array?
Toggle
Toggle button. Use { $bindState } on pressed for state binding.
label: stringpressed: boolean?variant: enum?
on.change
ToggleGroup
Group of toggle buttons. Type 'single' (default) or 'multiple'. Use { $bindState } on value.
items: arraytype: enum?value: string?
on.change
Tooltip
Hover tooltip. Shows content on hover over text.
content: stringtext: string
live renderstatic code
export
### Contact Us
Name
Email
`npm install @json-render/core @json-render/react`
[Get Started](https://json-render.dev/docs) [GitHub](https://github.com/vercel-labs/json-render)
01
### Define Your Catalog
Set the guardrails. Define which components, actions, and data bindings AI can use.
02
### AI Generates
Describe what you want. AI generates JSON constrained to your catalog. Every interface is unique.
03
### Render Instantly
Stream the response. Your components render progressively as JSON arrives.
## Define your catalog
Components, actions, and validation functions.
```
import { defineSchema, defineCatalog } from '@json-render/core';
import { z } from 'zod';
const schema = defineSchema({ /* ... */ });
export const catalog = defineCatalog(schema, {
components: {
Card: {
props: z.object({
title: z.string(),
description: z.string().nullable(),
}),
hasChildren: true,
},
Metric: {
props: z.object({
label: z.string(),
statePath: z.string(),
format: z.enum(['currency', 'percent']),
}),
},
},
actions: {
export: { params: z.object({ format: z.string() }) },
},
});
```
Show all
## AI generates JSON
Constrained output that your components render natively.
```
{
"root": "dashboard",
"elements": {
"dashboard": {
"type": "Card",
"props": {
"title": "Revenue Dashboard"
},
"children": ["revenue"]
},
"revenue": {
"type": "Metric",
"props": {
"label": "Total Revenue",
"statePath": "/metrics/revenue",
"format": "currency"
}
}
}
}
```
## Export as Code
Export generated UI as standalone React components. No runtime dependencies required.
### Generated UI Tree
AI generates a JSON structure from the user's prompt.
```
{
"root": "card",
"elements": {
"card": {
"type": "Card",
"props": { "title": "Revenue" },
"children": ["metric", "chart"]
},
"metric": {
"type": "Metric",
"props": {
"label": "Total Revenue",
"statePath": "analytics/revenue",
"format": "currency"
}
},
"chart": {
"type": "Chart",
"props": {
"statePath": "analytics/salesByRegion"
}
}
}
}
```
Show all
### Exported React Code
Export as a standalone Next.js project with all components.
```
"use client";
import { Card, Metric, Chart } from "@/components/ui";
const data = {
analytics: {
revenue: 125000,
salesByRegion: [\
{ label: "US", value: 45000 },\
{ label: "EU", value: 35000 },\
],
},
};
export default function Page() {
return (
<Card data={data} title="Revenue">
<Metric
data={data}
label="Total Revenue"
statePath="analytics/revenue"
format="currency"
/>
<Chart data={data} statePath="analytics/salesByRegion" />
</Card>
);
}
```
Show all
The export includes`package.json`, component files, styles, and everything needed to run independently.
## Features
### Generative UI
Generate dynamic, personalized interfaces from prompts with AI
### Guardrails
AI can only use components you define in the catalog
### Streaming
Progressive rendering as JSON streams from the model
### React & React Native
Render on web and mobile from the same catalog and spec format
### Data Binding
Connect props to state with $state, $item, $index, and two-way binding
### Code Export
Export as standalone React code with no runtime dependencies
## Get started
`npm install @json-render/core @json-render/react`
[Documentation](https://json-render.dev/docs)
Ask AI `⌘I`