- 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>
10 KiB
JSON-First Architecture
Overview
This low-code platform uses a JSON-first architecture where the entire application is defined declaratively in JSON, eliminating React boilerplate and enabling visual editing, version control, and runtime customization.
Core Principles
1. Everything is JSON
- Pages: All 35 application pages defined in JSON schemas
- Components: Atomic design library (atoms, molecules, organisms) in JSON
- Themes: Complete theming system configurable via JSON
- Data: State, bindings, and data sources declared in JSON
- Actions: Event handlers and side effects defined in JSON
2. Composition via $ref
JSON files reference each other using JSON Schema $ref:
{
"id": "dashboard",
"components": [
{ "$ref": "./molecules/dashboard-header.json" },
{ "$ref": "./molecules/stats-grid.json" }
]
}
3. One Definition Per File
Following single-responsibility principle:
- 1 function per TypeScript file
- 1 type per TypeScript file
- 1 component definition per JSON file
- Compose larger structures via $ref
Architecture Layers
┌─────────────────────────────────────┐
│ pages.json (35 pages) │ ← Router configuration
└──────────────┬──────────────────────┘
│ references
┌──────────────▼──────────────────────┐
│ Page Schemas (55 .json files) │ ← Page definitions
└──────────────┬──────────────────────┘
│ compose via $ref
┌──────────────▼──────────────────────┐
│ Organisms (8 .json files) │ ← Complex layouts
└──────────────┬──────────────────────┘
│ compose via $ref
┌──────────────▼──────────────────────┐
│ Molecules (23 .json files) │ ← Composed components
└──────────────┬──────────────────────┘
│ compose via $ref
┌──────────────▼──────────────────────┐
│ Atoms (23 .json files) │ ← Base components
└──────────────┬──────────────────────┘
│ reference
┌──────────────▼──────────────────────┐
│ React Components (68 .tsx) │ ← Implementation
│ Component Registry (100+ mapped) │
└─────────────────────────────────────┘
File Structure
src/config/pages/
├── atoms/ # 23 base components
│ ├── button-primary.json
│ ├── heading-1.json
│ ├── text-muted.json
│ └── ...
├── molecules/ # 23 composed components
│ ├── dashboard-header.json
│ ├── stats-grid.json
│ ├── stat-card-base.json
│ └── ...
├── organisms/ # 8 complex layouts
│ ├── app-header.json
│ ├── navigation-menu.json
│ └── ...
├── layouts/ # Layout templates
│ └── single-column.json
├── data-sources/ # Data source templates
│ └── kv-storage.json
└── *.json # 55 page schemas
├── dashboard-simple.json
├── settings-page.json
└── ...
JSON Schema Features
Page Schema
{
"$schema": "./schema/page-schema.json",
"id": "dashboard-simple",
"name": "Project Dashboard",
"description": "Overview of your project",
"icon": "ChartBar",
"layout": {
"$ref": "./layouts/single-column.json"
},
"dataSources": [
{
"id": "projectStats",
"$ref": "./data-sources/kv-storage.json",
"key": "project-stats",
"defaultValue": { "files": 0, "models": 0 }
}
],
"components": [
{ "$ref": "./molecules/dashboard-header.json" },
{ "$ref": "./molecules/stats-grid.json" }
]
}
Data Binding
{
"id": "files-value",
"type": "div",
"props": {
"className": "text-2xl font-bold",
"children": "0"
},
"dataBinding": {
"source": "projectStats",
"path": "files"
}
}
Actions
{
"type": "Button",
"events": [
{
"event": "onClick",
"actions": [
{
"type": "setState",
"target": "selectedTab",
"value": "colors"
},
{
"type": "toast",
"title": "Tab changed",
"variant": "success"
}
]
}
]
}
Conditionals
{
"type": "div",
"conditional": {
"source": "customColorCount",
"operator": "eq",
"value": 0
},
"children": [
{ "type": "p", "children": "No custom colors" }
]
}
Theming System
JSON Theme Definition
The entire theming system is JSON-based (theme.json):
{
"sidebar": {
"width": "16rem",
"backgroundColor": "oklch(0.19 0.02 265)",
"foregroundColor": "oklch(0.95 0.01 265)"
},
"colors": {
"primary": "oklch(0.58 0.24 265)",
"accent": "oklch(0.75 0.20 145)",
"background": "oklch(0.15 0.02 265)"
},
"typography": {
"fontFamily": {
"body": "'IBM Plex Sans', sans-serif",
"heading": "'JetBrains Mono', monospace"
}
},
"spacing": {
"radius": "0.5rem"
}
}
Runtime Theme Editing
Users can create theme variants and customize colors/fonts via JSON:
{
"activeVariantId": "dark",
"variants": [
{
"id": "dark",
"name": "Dark Mode",
"colors": {
"primary": "#7c3aed",
"secondary": "#38bdf8",
"customColors": {
"success": "#10b981",
"warning": "#f59e0b"
}
}
}
]
}
Data Sources
KV Storage
{
"id": "userData",
"type": "kv",
"key": "user-settings",
"defaultValue": { "theme": "dark" }
}
Computed Sources
{
"id": "totalFiles",
"type": "computed",
"compute": "(data) => data.files.length",
"dependencies": ["files"]
}
Static Sources
{
"id": "tabs",
"type": "static",
"defaultValue": ["colors", "typography", "preview"]
}
Benefits Over Traditional React
Traditional React Component (~50 lines)
import { useState } from 'react'
import { Card } from '@/components/ui/card'
interface DashboardProps {
initialData?: { files: number }
}
export function Dashboard({ initialData }: DashboardProps) {
const [stats, setStats] = useState(initialData || { files: 0 })
return (
<div className="p-6">
<div className="border-b pb-4">
<h1 className="text-2xl font-bold">Dashboard</h1>
</div>
<Card className="p-6">
<div className="text-2xl font-bold">{stats.files}</div>
<div className="text-sm text-muted">Files</div>
</Card>
</div>
)
}
JSON Equivalent (~15 lines)
{
"id": "dashboard",
"dataSources": [
{ "id": "stats", "type": "kv", "key": "stats" }
],
"components": [
{ "$ref": "./molecules/dashboard-header.json" },
{
"$ref": "./molecules/stat-card.json",
"dataBinding": { "source": "stats", "path": "files" }
}
]
}
Eliminated Boilerplate
✅ No imports - Components referenced by type string ✅ No TypeScript interfaces - Types inferred from registry ✅ No useState/useEffect - State declared in dataSources ✅ No event handlers - Actions declared in events array ✅ No prop drilling - Data binding handles it ✅ No component exports - Automatic via registry ✅ No JSX nesting - Flat JSON structure with $ref
Coverage Statistics
- 35/35 pages use JSON schemas (100%)
- 0/35 pages use React component references
- 109 JSON component files created
- 23 atoms
- 23 molecules
- 8 organisms
- 55 page schemas
- 68 React components remain as implementation layer
Potential Cleanup Targets
Deprecated Files (Safe to Remove)
src/config/default-pages.json- Replaced by pages.jsonsrc/config/json-demo.json- Old demo filesrc/config/template-ui.json- Replaced by JSON schemas
Keep (Still Used)
src/config/pages.json- Active router configurationtheme.json- Active theming systemsrc/config/feature-toggle-settings.json- Feature flags- All JSON schemas in
src/config/pages/
Best Practices
1. Atomic Granularity
Break components into smallest reusable units:
❌ dashboard.json (monolithic)
✅ dashboard-header.json + stats-grid.json + stat-card.json
2. $ref Composition
Always compose via references, never inline:
❌ { "type": "div", "children": [ ... 50 lines ... ] }
✅ { "$ref": "./molecules/complex-section.json" }
3. Single Responsibility
One purpose per JSON file:
✅ stat-card-base.json (template)
✅ stat-card-files.json (specific instance)
✅ stat-card-models.json (specific instance)
4. Descriptive IDs
Use semantic IDs that describe purpose:
{ "id": "dashboard-header" } // ✅ Good
{ "id": "div-1" } // ❌ Bad
Future Enhancements
- Visual JSON editor for drag-and-drop page building
- Theme marketplace with sharable JSON themes
- Component library with searchable JSON snippets
- JSON validation and IntelliSense in VSCode
- Hot-reload JSON changes without app restart
- A/B testing via JSON variant switching
- Multi-tenant customization via tenant-specific JSONs
Conclusion
This JSON-first architecture transforms React development from code-heavy to configuration-driven, enabling:
- Visual editing without touching code
- Version control friendly (JSON diffs)
- Runtime customization (load different JSONs)
- Non-developer accessibility (JSON is readable)
- Rapid prototyping (compose existing pieces)
- Consistent patterns (enforced by schema)
All without sacrificing the power of React when you need it - complex interactive components can still be written in React and referenced from JSON.