Files
metabuilder/codegen/docs/architecture/JSON_ORCHESTRATION_GUIDE.md
johndoe6345789 a51130a127 feat: Add external low-code and postgres repositories
- codegen: Low-code React app with JSON-driven component system
- packagerepo: Schema-driven package repository with backend/frontend
- postgres: Next.js app with Drizzle ORM and PostgreSQL

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 16:48:52 +00:00

10 KiB

JSON-Driven Page Orchestration Guide

Overview

The JSON orchestration system allows you to define entire pages using JSON schemas, eliminating the need for writing React components for common patterns. This approach:

  • Reduces code complexity: Components stay under 150 LOC
  • Improves maintainability: Changes to page structure don't require code changes
  • Enables rapid prototyping: New pages can be created by editing JSON
  • Provides type safety: Full TypeScript validation of schemas
  • Facilitates testing: JSON schemas are easy to validate and test

Table of Contents

  1. Core Concepts
  2. Page Schema Structure
  3. Component Tree
  4. Data Sources
  5. Actions
  6. Bindings
  7. Events
  8. Hooks
  9. Examples
  10. Best Practices

Core Concepts

Page Schema

A page schema is a JSON object that completely describes a page's structure, data, and behavior:

interface PageSchema {
  id: string                      // Unique page identifier
  name: string                    // Display name
  description?: string            // Optional description
  layout: LayoutConfig            // How components are arranged
  components: ComponentSchema[]   // Tree of components
  data?: DataSource[]            // Data sources
  actions?: ActionConfig[]        // Available actions
  hooks?: HookConfig[]           // Custom hooks to use
  seedData?: Record<string, any> // Initial/example data
}

Component Schema

Each component in the tree is described by:

interface ComponentSchema {
  id: string                     // Unique component ID
  type: string                   // Component type (Button, Card, etc.)
  props?: Record<string, any>    // Component props
  children?: ComponentSchema[]   // Nested components
  bindings?: DataBinding[]       // Data bindings
  events?: EventHandler[]        // Event handlers
  condition?: string             // Conditional rendering
}

Page Schema Structure

Basic Structure

{
  "id": "my-page",
  "name": "My Page",
  "layout": {
    "type": "single"
  },
  "components": [
    {
      "id": "main",
      "type": "div",
      "props": {
        "className": "p-6"
      }
    }
  ]
}

Layout Types

Single Layout

All components in a single container:

{
  "layout": {
    "type": "single"
  }
}

Split Layout

Resizable panels:

{
  "layout": {
    "type": "split",
    "direction": "horizontal",
    "sizes": [30, 70]
  }
}

Tabs Layout

Tabbed interface:

{
  "layout": {
    "type": "tabs"
  },
  "components": [
    {
      "id": "tab1",
      "props": {
        "label": "Tab 1"
      }
    }
  ]
}

Grid Layout

CSS Grid:

{
  "layout": {
    "type": "grid",
    "gap": 16
  }
}

Component Tree

Available Components

  • UI Components: Button, Card, Input, Badge, Textarea
  • HTML Elements: div, span, h1, h2, h3, p
  • Custom: Register more in ComponentRenderer.tsx

Component Example

{
  "id": "submit-button",
  "type": "Button",
  "props": {
    "variant": "default",
    "size": "lg",
    "className": "w-full"
  },
  "children": [
    {
      "id": "button-text",
      "type": "span",
      "props": {
        "children": "Submit"
      }
    }
  ]
}

Nesting Components

{
  "id": "card",
  "type": "Card",
  "props": {
    "className": "p-4"
  },
  "children": [
    {
      "id": "title",
      "type": "h2",
      "props": {
        "children": "Title"
      }
    },
    {
      "id": "content",
      "type": "p",
      "props": {
        "children": "Content goes here"
      }
    }
  ]
}

Data Sources

Data sources define where data comes from:

KV Store

{
  "id": "files",
  "type": "kv",
  "key": "project-files",
  "defaultValue": []
}

Computed Data

{
  "id": "activeFile",
  "type": "computed",
  "dependencies": ["files", "activeFileId"],
  "compute": "context.files.find(f => f.id === context.activeFileId)"
}

Static Data

{
  "id": "greeting",
  "type": "static",
  "defaultValue": "Hello, World!"
}

AI-Generated Data

{
  "id": "suggestions",
  "type": "ai",
  "dependencies": ["userInput"],
  "compute": "Generate 3 suggestions based on: ${context.userInput}"
}

Actions

Actions define what happens when events occur:

Create Action

{
  "id": "add-model",
  "type": "create",
  "trigger": "button-click",
  "params": {
    "target": "Models",
    "data": {
      "id": "${Date.now()}",
      "name": "NewModel"
    }
  },
  "onSuccess": "show-success-toast"
}

Update Action

{
  "id": "update-file",
  "type": "update",
  "trigger": "input-change",
  "params": {
    "target": "Files",
    "id": "${context.activeFileId}",
    "data": {
      "content": "${event.value}"
    }
  }
}

Delete Action

{
  "id": "delete-item",
  "type": "delete",
  "trigger": "button-click",
  "params": {
    "target": "Models",
    "id": "${context.selectedId}"
  }
}

Navigate Action

{
  "id": "go-to-dashboard",
  "type": "navigate",
  "trigger": "button-click",
  "params": {
    "tab": "dashboard"
  }
}

AI Generate Action

{
  "id": "ai-generate",
  "type": "ai-generate",
  "trigger": "button-click",
  "params": {
    "prompt": "Generate a user registration form with validation",
    "target": "Components"
  }
}

Custom Action

{
  "id": "custom-logic",
  "type": "custom",
  "trigger": "button-click",
  "params": {
    "customParam": "value"
  },
  "handler": "myCustomHandler"
}

Bindings

Bindings connect data to component props:

Simple Binding

{
  "bindings": [
    {
      "source": "user.name",
      "target": "children"
    }
  ]
}

Transformed Binding

{
  "bindings": [
    {
      "source": "files",
      "target": "children",
      "transform": "value.length + ' files'"
    }
  ]
}

Multiple Bindings

{
  "bindings": [
    {
      "source": "user.name",
      "target": "value"
    },
    {
      "source": "user.isActive",
      "target": "disabled",
      "transform": "!value"
    }
  ]
}

Events

Events connect user interactions to actions:

Button Click

{
  "events": [
    {
      "event": "onClick",
      "action": "add-model"
    }
  ]
}

Input Change

{
  "events": [
    {
      "event": "onChange",
      "action": "update-content",
      "params": {
        "field": "name"
      }
    }
  ]
}

Multiple Events

{
  "events": [
    {
      "event": "onClick",
      "action": "select-item"
    },
    {
      "event": "onDoubleClick",
      "action": "edit-item"
    }
  ]
}

Hooks

Custom hooks can be included:

{
  "hooks": [
    {
      "id": "files-hook",
      "name": "useFiles",
      "exports": ["files", "addFile", "updateFile"]
    },
    {
      "id": "modal-hook",
      "name": "useModal",
      "params": {
        "defaultOpen": false
      },
      "exports": ["isOpen", "open", "close"]
    }
  ]
}

Examples

Simple List Page

{
  "id": "todo-list",
  "name": "Todo List",
  "layout": {
    "type": "single"
  },
  "components": [
    {
      "id": "container",
      "type": "div",
      "props": {
        "className": "p-6"
      },
      "children": [
        {
          "id": "header",
          "type": "div",
          "props": {
            "className": "flex justify-between mb-4"
          },
          "children": [
            {
              "id": "title",
              "type": "h1",
              "props": {
                "children": "My Todos"
              }
            },
            {
              "id": "add-button",
              "type": "Button",
              "events": [
                {
                  "event": "onClick",
                  "action": "add-todo"
                }
              ],
              "children": [
                {
                  "id": "button-text",
                  "type": "span",
                  "props": {
                    "children": "Add Todo"
                  }
                }
              ]
            }
          ]
        }
      ]
    }
  ],
  "actions": [
    {
      "id": "add-todo",
      "type": "create",
      "params": {
        "target": "Todos",
        "data": {
          "id": "${Date.now()}",
          "text": "New todo",
          "completed": false
        }
      }
    }
  ]
}

Master-Detail Page

{
  "id": "user-detail",
  "name": "User Detail",
  "layout": {
    "type": "split",
    "direction": "horizontal",
    "sizes": [30, 70]
  },
  "components": [
    {
      "id": "user-list",
      "type": "Card",
      "props": {
        "className": "h-full p-4"
      }
    },
    {
      "id": "user-details",
      "type": "Card",
      "props": {
        "className": "h-full p-4"
      },
      "condition": "context.selectedUser !== null"
    }
  ],
  "data": [
    {
      "id": "selectedUser",
      "type": "computed",
      "dependencies": ["users", "selectedUserId"],
      "compute": "context.users.find(u => u.id === context.selectedUserId)"
    }
  ]
}

Best Practices

1. Use Descriptive IDs

{
  "id": "submit-form-button"
}

2. Keep Components Focused

Break down complex UIs into smaller components.

3. Use Computed Data

Don't repeat logic - compute derived data:

{
  "id": "incomplete-count",
  "type": "computed",
  "compute": "context.todos.filter(t => !t.completed).length"
}

4. Handle Edge Cases

Use conditions for empty states:

{
  "condition": "context.items.length === 0"
}

5. Provide Seed Data

Include example data for testing:

{
  "seedData": {
    "exampleUser": {
      "id": "1",
      "name": "John Doe"
    }
  }
}

6. Use Action Chains

Link actions with onSuccess:

{
  "id": "create-and-navigate",
  "type": "create",
  "onSuccess": "navigate-to-detail"
}

7. Keep Transforms Simple

Complex logic should be in hooks, not transforms.

8. Document Your Schemas

Add descriptions to clarify intent:

{
  "description": "Main dashboard showing project stats"
}

Next Steps

  • See /src/config/pages/ for more examples
  • Check /src/hooks/orchestration/ for hook implementation
  • Refer to /src/types/page-schema.ts for full type definitions
  • Read REFACTOR_PHASE3.md for architecture details