Last updated: 2026-01-16
- Audience: Developers (intermediate to advanced)
- Scope: AI-driven component rendering via JSON schemas
- Non-scope: Manual component development, Storybook
- Owner: TBD (confirm)
- Review cadence: TBD (confirm)
The JSON Render system enables AI agents to dynamically generate UI components by outputting JSON schemas. This bridges the gap between AI-generated designs and React components, allowing ChatGPT to create functional interfaces without writing JSX code.
┌─────────────────┐
│ AI Agent │
│ (ChatGPT) │
└────────┬────────┘
│ Outputs JSON Schema
▼
┌─────────────────┐
│ JsonRender │
│ Component │
└────────┬────────┘
│ Looks up components
▼
┌─────────────────┐
│ Component │
│ Registry │
└────────┬────────┘
│ Returns React Components
▼
┌─────────────────┐
│ Rendered UI │
└─────────────────┘
A JSON object describing a React component:
interface ComponentSchema {
component: string; // Component name from registry
props?: Record<string, any>; // Component props
children?: ComponentSchema[] | string; // Nested components or text
}A mapping of component names to React components. The default registry includes all aStudio UI components.
The React component that interprets JSON schemas and renders them as React components.
import { JsonRender } from "@design-studio/json-render";
const schema = {
component: "Card",
children: [
{
component: "CardHeader",
children: [
{
component: "CardTitle",
children: "Hello World",
},
],
},
],
};
function App() {
return <JsonRender schema={schema} />;
}import { JsonRender, createRegistry } from "@design-studio/json-render";
import { MyCustomComponent } from "./components";
const registry = createRegistry();
registry.register("MyCustomComponent", MyCustomComponent);
function App() {
return <JsonRender schema={schema} registry={registry} />;
}AI can generate complete dashboard layouts:
{
"component": "div",
"props": { "className": "grid grid-cols-3 gap-4" },
"children": [
{
"component": "Card",
"children": [
{
"component": "CardHeader",
"children": [{ "component": "CardTitle", "children": "Revenue" }]
},
{
"component": "CardContent",
"children": "$45,231.89"
}
]
}
]
}AI can create forms based on user requirements:
{
"component": "Card",
"children": [
{
"component": "CardHeader",
"children": [{ "component": "CardTitle", "children": "User Profile" }]
},
{
"component": "CardContent",
"children": [
{
"component": "div",
"props": { "className": "space-y-4" },
"children": [
{
"component": "div",
"children": [
{ "component": "Label", "children": "Name" },
{ "component": "Input", "props": { "placeholder": "Enter name" } }
]
}
]
}
]
}
]
}AI can build interactive components:
{
"component": "Tabs",
"props": { "defaultValue": "overview" },
"children": [
{
"component": "TabsList",
"children": [
{ "component": "TabsTrigger", "props": { "value": "overview" }, "children": "Overview" },
{ "component": "TabsTrigger", "props": { "value": "analytics" }, "children": "Analytics" }
]
},
{
"component": "TabsContent",
"props": { "value": "overview" },
"children": [
{
"component": "Card",
"children": [{ "component": "CardContent", "children": "Overview content" }]
}
]
}
]
}// packages/widgets/src/widgets/ai-dashboard/main.tsx
import { mountWidget, WidgetBase, WidgetErrorBoundary } from "../../../shared/widget-base";
import { JsonRender } from "@design-studio/json-render";
import { useWidgetProps } from "../../../shared/use-widget-props";
import "../../../styles.css";
function AIDashboard() {
const props = useWidgetProps<{ schema: ComponentSchema }>();
return (
<WidgetErrorBoundary>
<WidgetBase title="AI Dashboard">
<JsonRender schema={props.schema} />
</WidgetBase>
</WidgetErrorBoundary>
);
}
mountWidget(<AIDashboard />);Create a dashboard for monitoring system health with:
- 3 metric cards showing CPU, Memory, and Disk usage
- A chart showing trends over time
- A table of recent alerts
Output as JSON schema using aStudio components.
See REGISTRY.md for the complete list of available components and their props.
- Use semantic component names: Prefer
Card,Buttonover genericdiv - Nest components properly: Follow component hierarchy (e.g.,
Card→CardHeader→CardTitle) - Include accessibility props: Add
aria-label,rolewhen appropriate - Use layout utilities: Leverage Tailwind classes in
classNameprops - Validate schemas: Ensure all component names exist in registry
- Extend the registry: Add custom components for domain-specific needs
- Provide fallbacks: Use the
fallbackprop for error handling - Type safety: Use TypeScript interfaces for schema validation
- Test schemas: Validate JSON schemas before rendering
- Document components: Keep registry documentation up-to-date
import { z } from "zod";
const ComponentSchemaValidator = z.object({
component: z.string(),
props: z.record(z.any()).optional(),
children: z.union([z.array(z.lazy(() => ComponentSchemaValidator)), z.string()]).optional(),
});
function validateSchema(schema: unknown) {
return ComponentSchemaValidator.parse(schema);
}import { createRegistry, defaultRegistry } from "@design-studio/json-render";
import { MetricCard, ChartWidget, DataTable } from "./domain-components";
const customRegistry = createRegistry();
// Copy default components
defaultRegistry.list().forEach((name) => {
const component = defaultRegistry.get(name);
if (component) {
customRegistry.register(name, component);
}
});
// Add domain-specific components
customRegistry.register("MetricCard", MetricCard);
customRegistry.register("ChartWidget", ChartWidget);
customRegistry.register("DataTable", DataTable);function SafeJsonRender({ schema }: { schema: ComponentSchema }) {
return (
<JsonRender
schema={schema}
fallback={
<Alert variant="destructive">
<AlertTitle>Rendering Error</AlertTitle>
<AlertDescription>
Failed to render the component. Please check the schema.
</AlertDescription>
</Alert>
}
/>
);
}Problem: Console warning "Component 'X' not found in registry"
Solution:
- Check component name spelling
- Verify component is registered in the registry
- Use
registry.list()to see available components
Problem: Component renders but props don't apply
Solution:
- Verify prop names match component API
- Check prop types (strings, numbers, booleans)
- Review component documentation in REGISTRY.md
Problem: Nested components don't appear
Solution:
- Ensure
childrenis an array of schemas or a string - Check for circular references in schema
- Verify parent component accepts children
- Schema size: Large schemas may impact initial render time
- Registry size: Keep registry focused on needed components
- Memoization: Consider memoizing schemas that don't change
- Lazy loading: Load domain-specific registries on demand
- Validate schema structure
- Sanitize user input in props
- Whitelist allowed components
- Limit schema depth to prevent stack overflow
- REGISTRY.md - Complete component registry
- AI_GUIDE.md - Guide for AI agents
- EXAMPLES.md - Example schemas and patterns
- Pages Quick Start - Creating widgets
To add new components to the default registry:
- Import component from
@design-studio/ui - Register in
packages/json-render/src/default-registry.ts - Document in
docs/json-render/REGISTRY.md - Add examples in
docs/json-render/EXAMPLES.md - Update tests
For issues or questions:
- Check EXAMPLES.md for common patterns
- Review REGISTRY.md for component APIs
- See AI_GUIDE.md for AI-specific guidance