+ {/* 1. Sidebar: Example Selector */}
+
+
+ {/* 2. Middle: Code Editor */}
+
+
+
+
+
JSON Schema
+
+
+
+
+ {jsonError && (
+
+ JSON Error: {jsonError}
+
+ )}
+
+
+
+
+
+ {/* 3. Right: Live Preview */}
+
+
+
Preview
+
+ {/* Viewport Size Toggles */}
+
+
+
+
+
+
+
+
+
+
+ {schema && !jsonError ? (
+
+ ) : (
+
+ {jsonError ? (
+
+
Invalid JSON
+
Fix the syntax error to see the preview
+
+ ) : (
+
Select an example to get started
+ )}
+
+ )}
+
+
+
+
+
+ );
+}
diff --git a/apps/playground/src/data/examples.ts b/apps/playground/src/data/examples.ts
new file mode 100644
index 000000000..1af817fab
--- /dev/null
+++ b/apps/playground/src/data/examples.ts
@@ -0,0 +1,650 @@
+/**
+ * Predefined JSON schema examples for the Object UI Playground
+ * Organized by category to showcase different capabilities
+ */
+
+export const examples = {
+ // A. Basic Primitives - Showcase Shadcn component wrapping
+ 'input-states': `{
+ "type": "div",
+ "className": "space-y-6 max-w-md",
+ "body": [
+ {
+ "type": "div",
+ "className": "space-y-2",
+ "body": [
+ {
+ "type": "text",
+ "content": "Input Component States",
+ "className": "text-2xl font-bold"
+ },
+ {
+ "type": "text",
+ "content": "Demonstrates various input states and configurations",
+ "className": "text-muted-foreground"
+ }
+ ]
+ },
+ {
+ "type": "input",
+ "label": "Regular Input",
+ "id": "regular",
+ "placeholder": "Enter your name"
+ },
+ {
+ "type": "input",
+ "label": "Required Field",
+ "id": "required",
+ "required": true,
+ "placeholder": "This field is required"
+ },
+ {
+ "type": "input",
+ "label": "Disabled Input",
+ "id": "disabled",
+ "disabled": true,
+ "value": "Cannot edit this"
+ },
+ {
+ "type": "input",
+ "label": "Email Input",
+ "id": "email",
+ "inputType": "email",
+ "placeholder": "user@example.com"
+ }
+ ]
+}`,
+
+ 'button-variants': `{
+ "type": "div",
+ "className": "space-y-6 max-w-2xl",
+ "body": [
+ {
+ "type": "div",
+ "className": "space-y-2",
+ "body": [
+ {
+ "type": "text",
+ "content": "Button Variants",
+ "className": "text-2xl font-bold"
+ },
+ {
+ "type": "text",
+ "content": "Different button styles using Shadcn variants",
+ "className": "text-muted-foreground"
+ }
+ ]
+ },
+ {
+ "type": "div",
+ "className": "space-y-4",
+ "body": [
+ {
+ "type": "div",
+ "className": "flex flex-wrap gap-2",
+ "body": [
+ {
+ "type": "button",
+ "label": "Default"
+ },
+ {
+ "type": "button",
+ "label": "Destructive",
+ "variant": "destructive"
+ },
+ {
+ "type": "button",
+ "label": "Outline",
+ "variant": "outline"
+ },
+ {
+ "type": "button",
+ "label": "Secondary",
+ "variant": "secondary"
+ },
+ {
+ "type": "button",
+ "label": "Ghost",
+ "variant": "ghost"
+ },
+ {
+ "type": "button",
+ "label": "Link",
+ "variant": "link"
+ }
+ ]
+ },
+ {
+ "type": "div",
+ "className": "space-y-2",
+ "body": [
+ {
+ "type": "text",
+ "content": "Tailwind Native: Custom Styling",
+ "className": "text-sm font-semibold"
+ },
+ {
+ "type": "button",
+ "label": "Purple Custom Button",
+ "className": "bg-purple-500 hover:bg-purple-700 text-white"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}`,
+
+ // B. Complex Layouts - The killer feature
+ 'grid-layout': `{
+ "type": "div",
+ "className": "space-y-6",
+ "body": [
+ {
+ "type": "div",
+ "className": "space-y-2",
+ "body": [
+ {
+ "type": "text",
+ "content": "Responsive Grid Layout",
+ "className": "text-2xl font-bold"
+ },
+ {
+ "type": "text",
+ "content": "Complex nested grid with responsive breakpoints",
+ "className": "text-muted-foreground"
+ }
+ ]
+ },
+ {
+ "type": "div",
+ "className": "grid gap-4 md:grid-cols-2 lg:grid-cols-3",
+ "body": [
+ {
+ "type": "card",
+ "className": "md:col-span-2",
+ "title": "Wide Card",
+ "description": "This card spans 2 columns on medium screens",
+ "body": {
+ "type": "div",
+ "className": "p-6 pt-0",
+ "body": {
+ "type": "text",
+ "content": "Try resizing your browser to see the responsive behavior!"
+ }
+ }
+ },
+ {
+ "type": "card",
+ "title": "Regular Card",
+ "body": {
+ "type": "div",
+ "className": "p-6 pt-0",
+ "body": {
+ "type": "text",
+ "content": "Standard card"
+ }
+ }
+ },
+ {
+ "type": "card",
+ "title": "Card 1",
+ "body": {
+ "type": "div",
+ "className": "p-6 pt-0",
+ "body": {
+ "type": "text",
+ "content": "Content 1"
+ }
+ }
+ },
+ {
+ "type": "card",
+ "title": "Card 2",
+ "body": {
+ "type": "div",
+ "className": "p-6 pt-0",
+ "body": {
+ "type": "text",
+ "content": "Content 2"
+ }
+ }
+ },
+ {
+ "type": "card",
+ "title": "Card 3",
+ "body": {
+ "type": "div",
+ "className": "p-6 pt-0",
+ "body": {
+ "type": "text",
+ "content": "Content 3"
+ }
+ }
+ }
+ ]
+ }
+ ]
+}`,
+
+ 'dashboard': `{
+ "type": "div",
+ "className": "space-y-6",
+ "body": [
+ {
+ "type": "div",
+ "className": "flex items-center justify-between",
+ "body": [
+ {
+ "type": "div",
+ "className": "space-y-1",
+ "body": [
+ {
+ "type": "text",
+ "content": "Analytics Dashboard",
+ "className": "text-2xl font-bold tracking-tight"
+ },
+ {
+ "type": "text",
+ "content": "Overview of your project performance and metrics.",
+ "className": "text-sm text-muted-foreground"
+ }
+ ]
+ },
+ {
+ "type": "div",
+ "className": "flex items-center gap-2",
+ "body": [
+ {
+ "type": "button",
+ "label": "Download",
+ "variant": "outline",
+ "size": "sm"
+ },
+ {
+ "type": "button",
+ "label": "Create Report",
+ "size": "sm"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "div",
+ "className": "grid gap-4 md:grid-cols-2 lg:grid-cols-4",
+ "body": [
+ {
+ "type": "card",
+ "className": "shadow-sm hover:shadow-md transition-shadow",
+ "body": [
+ {
+ "type": "div",
+ "className": "p-6 pb-2",
+ "body": {
+ "type": "text",
+ "content": "Total Revenue",
+ "className": "text-sm font-medium text-muted-foreground"
+ }
+ },
+ {
+ "type": "div",
+ "className": "p-6 pt-0",
+ "body": [
+ {
+ "type": "text",
+ "content": "$45,231.89",
+ "className": "text-2xl font-bold"
+ },
+ {
+ "type": "text",
+ "content": "+20.1% from last month",
+ "className": "text-xs text-muted-foreground mt-1"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "card",
+ "className": "shadow-sm hover:shadow-md transition-shadow",
+ "body": [
+ {
+ "type": "div",
+ "className": "p-6 pb-2",
+ "body": {
+ "type": "text",
+ "content": "Subscriptions",
+ "className": "text-sm font-medium text-muted-foreground"
+ }
+ },
+ {
+ "type": "div",
+ "className": "p-6 pt-0",
+ "body": [
+ {
+ "type": "text",
+ "content": "+2,350",
+ "className": "text-2xl font-bold"
+ },
+ {
+ "type": "text",
+ "content": "+180.1% from last month",
+ "className": "text-xs text-muted-foreground mt-1"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "card",
+ "className": "shadow-sm hover:shadow-md transition-shadow",
+ "body": [
+ {
+ "type": "div",
+ "className": "p-6 pb-2",
+ "body": {
+ "type": "text",
+ "content": "Sales",
+ "className": "text-sm font-medium text-muted-foreground"
+ }
+ },
+ {
+ "type": "div",
+ "className": "p-6 pt-0",
+ "body": [
+ {
+ "type": "text",
+ "content": "+12,234",
+ "className": "text-2xl font-bold"
+ },
+ {
+ "type": "text",
+ "content": "+19% from last month",
+ "className": "text-xs text-muted-foreground mt-1"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "card",
+ "className": "shadow-sm hover:shadow-md transition-shadow",
+ "body": [
+ {
+ "type": "div",
+ "className": "p-6 pb-2",
+ "body": {
+ "type": "text",
+ "content": "Active Now",
+ "className": "text-sm font-medium text-muted-foreground"
+ }
+ },
+ {
+ "type": "div",
+ "className": "p-6 pt-0",
+ "body": [
+ {
+ "type": "text",
+ "content": "+573",
+ "className": "text-2xl font-bold"
+ },
+ {
+ "type": "text",
+ "content": "+201 since last hour",
+ "className": "text-xs text-muted-foreground mt-1"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "card",
+ "className": "shadow-sm",
+ "title": "Recent Activity",
+ "description": "Your latest component interactions",
+ "body": {
+ "type": "div",
+ "className": "p-6 pt-0 space-y-2",
+ "body": [
+ {
+ "type": "text",
+ "content": "Schema updated successfully",
+ "className": "text-sm"
+ },
+ {
+ "type": "text",
+ "content": "New component rendered at 10:42 AM",
+ "className": "text-sm text-muted-foreground"
+ }
+ ]
+ }
+ }
+ ]
+}`,
+
+ 'tabs-demo': `{
+ "type": "div",
+ "className": "space-y-6",
+ "body": [
+ {
+ "type": "text",
+ "content": "Tabs Component",
+ "className": "text-2xl font-bold"
+ },
+ {
+ "type": "tabs",
+ "defaultValue": "account",
+ "className": "w-full",
+ "items": [
+ {
+ "value": "account",
+ "label": "Account",
+ "body": {
+ "type": "card",
+ "title": "Account Settings",
+ "description": "Make changes to your account here.",
+ "body": {
+ "type": "div",
+ "className": "p-6 pt-0 space-y-4",
+ "body": [
+ {
+ "type": "input",
+ "label": "Name",
+ "id": "name",
+ "value": "Pedro Duarte"
+ },
+ {
+ "type": "input",
+ "label": "Username",
+ "id": "username",
+ "value": "@peduarte"
+ },
+ {
+ "type": "button",
+ "label": "Save changes"
+ }
+ ]
+ }
+ }
+ },
+ {
+ "value": "password",
+ "label": "Password",
+ "body": {
+ "type": "card",
+ "title": "Password",
+ "description": "Change your password here.",
+ "body": {
+ "type": "div",
+ "className": "p-6 pt-0 space-y-4",
+ "body": [
+ {
+ "type": "input",
+ "label": "Current password",
+ "id": "current",
+ "inputType": "password"
+ },
+ {
+ "type": "input",
+ "label": "New password",
+ "id": "new",
+ "inputType": "password"
+ },
+ {
+ "type": "button",
+ "label": "Update password"
+ }
+ ]
+ }
+ }
+ },
+ {
+ "value": "notifications",
+ "label": "Notifications",
+ "body": {
+ "type": "card",
+ "title": "Notifications",
+ "description": "Configure how you receive notifications.",
+ "body": {
+ "type": "div",
+ "className": "p-6 pt-0",
+ "body": {
+ "type": "text",
+ "content": "Notification settings will be displayed here."
+ }
+ }
+ }
+ }
+ ]
+ }
+ ]
+}`,
+
+ // C. Form with simple structure (no data linkage for now as that requires runtime state)
+ 'form-demo': `{
+ "type": "div",
+ "className": "max-w-2xl space-y-6",
+ "body": [
+ {
+ "type": "div",
+ "className": "space-y-2",
+ "body": [
+ {
+ "type": "text",
+ "content": "User Registration Form",
+ "className": "text-2xl font-bold"
+ },
+ {
+ "type": "text",
+ "content": "A comprehensive form demonstrating various input types",
+ "className": "text-muted-foreground"
+ }
+ ]
+ },
+ {
+ "type": "card",
+ "className": "shadow-sm",
+ "body": {
+ "type": "div",
+ "className": "p-6 space-y-6",
+ "body": [
+ {
+ "type": "div",
+ "className": "grid gap-4 md:grid-cols-2",
+ "body": [
+ {
+ "type": "input",
+ "label": "First Name",
+ "id": "firstName",
+ "required": true,
+ "placeholder": "John"
+ },
+ {
+ "type": "input",
+ "label": "Last Name",
+ "id": "lastName",
+ "required": true,
+ "placeholder": "Doe"
+ }
+ ]
+ },
+ {
+ "type": "input",
+ "label": "Email Address",
+ "id": "email",
+ "inputType": "email",
+ "required": true,
+ "placeholder": "john.doe@example.com"
+ },
+ {
+ "type": "input",
+ "label": "Password",
+ "id": "password",
+ "inputType": "password",
+ "required": true,
+ "placeholder": "••••••••"
+ },
+ {
+ "type": "div",
+ "className": "flex items-center justify-end gap-2",
+ "body": [
+ {
+ "type": "button",
+ "label": "Cancel",
+ "variant": "outline"
+ },
+ {
+ "type": "button",
+ "label": "Create Account"
+ }
+ ]
+ }
+ ]
+ }
+ }
+ ]
+}`,
+
+ 'simple-page': `{
+ "type": "div",
+ "className": "space-y-4",
+ "body": [
+ {
+ "type": "text",
+ "content": "Welcome to Object UI",
+ "className": "text-3xl font-bold"
+ },
+ {
+ "type": "text",
+ "content": "The Universal, Schema-Driven Rendering Engine",
+ "className": "text-xl text-muted-foreground"
+ },
+ {
+ "type": "div",
+ "className": "flex gap-2 mt-4",
+ "body": [
+ {
+ "type": "button",
+ "label": "Get Started"
+ },
+ {
+ "type": "button",
+ "label": "Learn More",
+ "variant": "outline"
+ }
+ ]
+ }
+ ]
+}`
+};
+
+export type ExampleKey = keyof typeof examples;
+
+export const exampleCategories = {
+ 'Primitives': ['simple-page', 'input-states', 'button-variants'],
+ 'Layouts': ['grid-layout', 'dashboard', 'tabs-demo'],
+ 'Forms': ['form-demo']
+};
diff --git a/apps/playground/src/index.css b/apps/playground/src/index.css
new file mode 100644
index 000000000..d924e2434
--- /dev/null
+++ b/apps/playground/src/index.css
@@ -0,0 +1,69 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+@layer base {
+ :root {
+ --background: 0 0% 100%;
+ --foreground: 222.2 84% 4.9%;
+ --card: 0 0% 100%;
+ --card-foreground: 222.2 84% 4.9%;
+ --popover: 0 0% 100%;
+ --popover-foreground: 222.2 84% 4.9%;
+ --primary: 222.2 47.4% 11.2%;
+ --primary-foreground: 210 40% 98%;
+ --secondary: 210 40% 96.1%;
+ --secondary-foreground: 222.2 47.4% 11.2%;
+ --muted: 210 40% 96.1%;
+ --muted-foreground: 215.4 16.3% 46.9%;
+ --accent: 210 40% 96.1%;
+ --accent-foreground: 222.2 47.4% 11.2%;
+ --destructive: 0 84.2% 60.2%;
+ --destructive-foreground: 210 40% 98%;
+ --border: 214.3 31.8% 91.4%;
+ --input: 214.3 31.8% 91.4%;
+ --ring: 222.2 84% 4.9%;
+ --radius: 0.5rem;
+ --chart-1: 12 76% 61%;
+ --chart-2: 173 58% 39%;
+ --chart-3: 197 37% 24%;
+ --chart-4: 43 74% 66%;
+ --chart-5: 27 87% 67%;
+ }
+
+ .dark {
+ --background: 222.2 84% 4.9%;
+ --foreground: 210 40% 98%;
+ --card: 222.2 84% 4.9%;
+ --card-foreground: 210 40% 98%;
+ --popover: 222.2 84% 4.9%;
+ --popover-foreground: 210 40% 98%;
+ --primary: 210 40% 98%;
+ --primary-foreground: 222.2 47.4% 11.2%;
+ --secondary: 217.2 32.6% 17.5%;
+ --secondary-foreground: 210 40% 98%;
+ --muted: 217.2 32.6% 17.5%;
+ --muted-foreground: 215 20.2% 65.1%;
+ --accent: 217.2 32.6% 17.5%;
+ --accent-foreground: 210 40% 98%;
+ --destructive: 0 62.8% 30.6%;
+ --destructive-foreground: 210 40% 98%;
+ --border: 217.2 32.6% 17.5%;
+ --input: 217.2 32.6% 17.5%;
+ --ring: 212.7 26.8% 83.9%;
+ --chart-1: 220 70% 50%;
+ --chart-2: 160 60% 45%;
+ --chart-3: 30 80% 55%;
+ --chart-4: 280 65% 60%;
+ --chart-5: 340 75% 55%;
+ }
+}
+
+@layer base {
+ * {
+ @apply border-border;
+ }
+ body {
+ @apply bg-background text-foreground;
+ }
+}
diff --git a/apps/playground/src/main.tsx b/apps/playground/src/main.tsx
new file mode 100644
index 000000000..cc14d8376
--- /dev/null
+++ b/apps/playground/src/main.tsx
@@ -0,0 +1,10 @@
+import { StrictMode } from 'react';
+import { createRoot } from 'react-dom/client';
+import './index.css';
+import App from './App';
+
+createRoot(document.getElementById('root')!).render(
+