Schema Playground
Interactive schema playground for experimenting with ObjectUI schemas — edit JSON, see live UI, and learn by doing
Schema Playground
The Schema Playground is the fastest way to learn ObjectUI. Write JSON schemas in an editor, see the rendered UI instantly, and iterate on your designs without setting up a project.
How It Works
The playground follows the core ObjectUI rendering pipeline:
JSON Editor → Schema Validation → SchemaRenderer → Live Preview- Write a JSON schema in the left panel
- Preview the rendered UI in the right panel in real-time
- Iterate by modifying properties and seeing changes instantly
Setting Up the Playground
Add the playground to any React project with ObjectUI installed:
import { useState } from 'react';
import { SchemaRenderer } from '@object-ui/react';
import { registerDefaultRenderers } from '@object-ui/components';
import { registerAllFields } from '@object-ui/fields';
import { Registry } from '@object-ui/core';
registerDefaultRenderers();
registerAllFields(Registry);
function SchemaPlayground() {
const [schema, setSchema] = useState('{\n "type": "button",\n "label": "Click me"\n}');
const [error, setError] = useState<string | null>(null);
const parsed = (() => {
try {
const obj = JSON.parse(schema);
if (error) setError(null);
return obj;
} catch (e) {
setError((e as Error).message);
return null;
}
})();
return (
<div className="grid grid-cols-2 gap-4 h-[600px]">
<div className="flex flex-col gap-2">
<h3 className="text-sm font-medium">JSON Schema</h3>
<textarea
className="flex-1 rounded-md border bg-muted p-3 font-mono text-sm"
value={schema}
onChange={(e) => setSchema(e.target.value)}
/>
{error && (
<p className="text-sm text-destructive">{error}</p>
)}
</div>
<div className="flex flex-col gap-2">
<h3 className="text-sm font-medium">Live Preview</h3>
<div className="flex-1 rounded-md border bg-background p-4 overflow-auto">
{parsed && <SchemaRenderer schema={parsed} />}
</div>
</div>
</div>
);
}Example Schemas
Try pasting any of the following schemas into the playground to see them rendered.
Button
A simple button with a variant and icon:
{
"type": "button",
"label": "Save Changes",
"variant": "default",
"icon": "save",
"size": "default"
}Try changing "variant" to "destructive", "outline", "secondary", or "ghost" to see different styles.
Card
A content card with a header, description, and body:
{
"type": "card",
"title": "Monthly Revenue",
"description": "Revenue summary for the current month",
"icon": "dollar-sign",
"body": {
"type": "stack",
"direction": "vertical",
"gap": "md",
"items": [
{
"type": "text",
"content": "$48,250",
"className": "text-3xl font-bold"
},
{
"type": "badge",
"label": "+12.5% from last month",
"variant": "secondary"
}
]
}
}Form
A complete form with validation, driven entirely by schema:
{
"type": "form",
"title": "Create Account",
"fields": [
{
"name": "fullName",
"type": "text",
"label": "Full Name",
"required": true,
"placeholder": "Jane Doe"
},
{
"name": "email",
"type": "text",
"label": "Email",
"required": true,
"placeholder": "jane@example.com",
"validation": {
"pattern": "^[^@]+@[^@]+\\.[^@]+$",
"message": "Enter a valid email address"
}
},
{
"name": "role",
"type": "select",
"label": "Role",
"options": ["Admin", "Editor", "Viewer"],
"defaultValue": "Viewer"
},
{
"name": "bio",
"type": "textarea",
"label": "Bio",
"placeholder": "Tell us about yourself..."
}
],
"actions": [
{ "type": "submit", "label": "Create Account", "variant": "default" },
{ "type": "reset", "label": "Reset", "variant": "outline" }
]
}Grid
A data grid with sortable columns and row actions:
{
"type": "grid",
"title": "Team Members",
"columns": [
{ "field": "name", "label": "Name", "sortable": true },
{ "field": "role", "label": "Role", "sortable": true },
{ "field": "status", "label": "Status", "sortable": true },
{ "field": "joined", "label": "Joined", "type": "date" }
],
"rows": [
{ "name": "Alice Chen", "role": "Engineer", "status": "Active", "joined": "2024-01-15" },
{ "name": "Bob Smith", "role": "Designer", "status": "Active", "joined": "2024-03-22" },
{ "name": "Carol Jones", "role": "PM", "status": "Away", "joined": "2023-11-08" }
],
"actions": [
{ "label": "Edit", "icon": "pencil", "action": "edit" },
{ "label": "Delete", "icon": "trash-2", "action": "delete", "variant": "destructive" }
],
"pagination": {
"pageSize": 10,
"showPageSizeOptions": true
}
}Composing Schemas
Schemas can be nested to build complex layouts. Here is a dashboard that combines multiple components:
{
"type": "page",
"title": "Project Dashboard",
"body": {
"type": "grid-layout",
"columns": 3,
"gap": "md",
"items": [
{
"type": "card",
"title": "Open Issues",
"body": {
"type": "text",
"content": "24",
"className": "text-4xl font-bold text-primary"
}
},
{
"type": "card",
"title": "Pull Requests",
"body": {
"type": "text",
"content": "8",
"className": "text-4xl font-bold text-primary"
}
},
{
"type": "card",
"title": "Deployments",
"body": {
"type": "text",
"content": "142",
"className": "text-4xl font-bold text-primary"
}
}
]
}
}Schema → Rendered UI Flow
Every schema goes through the following pipeline before becoming visible UI:
┌─────────────┐ ┌──────────────┐ ┌────────────────┐ ┌──────────┐
│ JSON Schema │────▶│ Validation │────▶│ Registry Lookup│────▶│ React UI │
└─────────────┘ └──────────────┘ └────────────────┘ └──────────┘- Parse — The JSON string is parsed into a JavaScript object.
- Validate — The
typefield is checked against the component registry. Required fields are verified. - Resolve — The registry maps the
typeto a React component. Expressions like"visibleOn": "${data.role === 'admin'}"are evaluated. - Render — The matched component receives the schema properties as props and renders Shadcn primitives with Tailwind classes.
In code, this is a single call:
import { SchemaRenderer } from '@object-ui/react';
// The schema object (from your editor, API, or file)
const schema = {
type: 'card',
title: 'Hello',
body: { type: 'text', content: 'World' },
};
// One line to go from JSON to UI
<SchemaRenderer schema={schema} />Tips for the Playground
- Start simple — Begin with a single
buttonortextschema and add complexity incrementally. - Use
className— Any schema object accepts aclassNameproperty for Tailwind utility classes. - Check the
type— If nothing renders, verify thetypevalue matches a registered component. - Nest schemas — Use container types like
stack,grid-layout, andpageto compose multiple components. - Add expressions — Use
visibleOnanddisabledOnfor dynamic behavior:"visibleOn": "${data.showAdvanced}".
Related Resources
- Schema Rendering — How the rendering engine works in detail
- Schema Overview — Full catalog of all available schema types
- Component Registry — How components are registered and resolved
- Expressions — Dynamic expressions for conditional rendering
- Building a CRUD App — End-to-end tutorial using schemas in a real project
- Type Documentation — Full API reference for
@object-ui/types