- 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
597 lines
11 KiB
Markdown
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` |