The AI schema layer for ai-design-system. Provides everything a consumer app needs to let an AI model generate a UI — except the AI call itself.
ai-design-system component implementations (React + Tailwind)
↑ peer dep
design-schema schema definitions + React registries + prompt helpers ← this package
↑ npm dep
waypoint-builder (or any consumer app)
An AI model needs three things to generate a UI reliably:
- A system prompt — describing exactly which components exist and what props they accept
- A rendered output — a React registry that maps the AI's JSON output to real components
- A storage contract — a TypeScript type for what gets persisted to the DB
design-schema provides all three. The AI call, state management, and persistence are owned entirely by the consumer.
- ❌ Make any AI / LLM calls
- ❌ Manage application state (no Jotai atoms, no Zustand)
- ❌ Write to any database
- ❌ Implement any business logic
{
"ai-design-system": "*",
"@json-render/core": "*",
"@json-render/react": "*",
"@json-render/shadcn": "*",
"zod": "^3",
"react": "^18 || ^19"
}@json-render/shadcn covers the 30 generic shadcn primitives (Button, Card, Input, Grid, etc.).
design-schema extends it with ai-design-system-specific components (StatsCard, InteractiveChart, DataTable, WorkflowBuilder nodes, AI conversation elements).
design-schema/
├── src/
│ ├── schemas/ # Component definitions (Zod schemas + AI descriptions)
│ │ ├── core/ # Re-exports @json-render/shadcn + layout overrides
│ │ ├── data/ # StatsCard, InteractiveChart, DataTable
│ │ ├── workflow/ # StateNode, TransitionNode, WorkflowToolbar
│ │ └── ai/ # PromptInput, Message, CodeBlock, ChainOfThought
│ │
│ ├── registries/
│ │ └── react/ # Wires each schema domain to ai-design-system components
│ │ ├── core.tsx
│ │ ├── data.tsx
│ │ ├── workflow.tsx
│ │ └── ai.tsx
│ │
│ ├── presets/ # Pre-assembled schema + registry bundles
│ │ ├── dashboard/ # core + data + feedback
│ │ ├── form/ # core + forms + feedback
│ │ ├── workflow/ # core + workflow + ai
│ │ └── full/ # Everything
│ │
│ ├── prompts/ # System prompt builders
│ │ └── index.ts # getSystemPrompt(), buildUserPrompt()
│ │
│ ├── store/ # DB storage contract
│ │ └── types.ts # SchemaRecord, SchemaStore interface
│ │
│ └── adapters/ # Optional drop-in helpers
│ ├── jotai/ # createSchemaAtoms(store) — Jotai atom factory
│ └── drizzle/ # schemaColumn() — Drizzle JSONB column type helper
│
├── package.json
├── vite.config.ts
└── README.md
"." → full preset (dashboard) + store types
"./schemas/core" → layout primitives (backend-safe, no React)
"./schemas/data" → data display schemas
"./schemas/workflow" → workflow component schemas
"./schemas/ai" → AI conversation schemas
"./registries/react/core" → React registry for core
"./registries/react/data" → React registry for data
"./registries/react/workflow" → React registry for workflow
"./registries/react/ai" → React registry for AI elements
"./presets/dashboard" → dashboard catalog + registry + system prompt
"./presets/form" → form catalog + registry + system prompt
"./presets/workflow" → workflow catalog + registry + system prompt
"./presets/full" → all domains bundled
"./prompts" → buildSystemPrompt(), buildUserPrompt()
"./store" → SchemaRecord type + SchemaStore interface
"./adapters/jotai" → optional Jotai atom factories
"./adapters/drizzle" → optional Drizzle column helper
Schemas (./schemas/*) are backend-safe — they import no React. Safe to use in a server-side route for building the LLM system prompt without pulling any component code into the backend bundle.
// waypoint-builder-backend/src/routes/generate-ui.ts
import { dashboardPreset, buildUserPrompt } from 'design-schema/presets/dashboard';
import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';
app.post('/api/generate-ui', async (c) => {
const { prompt, currentSpec } = await c.req.json();
const result = streamText({
model: openai('gpt-4o'),
system: dashboardPreset.getSystemPrompt(), // design-schema builds this
prompt: buildUserPrompt({ prompt, currentSpec }), // handles fresh vs. refinement
});
return result.toDataStreamResponse();
});// waypoint-builder-frontend/src/components/AIUICanvas.tsx
import { dashboardPreset } from 'design-schema/presets/dashboard';
import { Renderer } from '@json-render/react';
export function AIUICanvas({ spec }) {
return (
<Renderer
registry={dashboardPreset.registry} // design-schema wires this
spec={spec} // consumer manages this (Jotai atom, useState, etc.)
/>
);
}// waypoint-builder-frontend/src/lib/schema-store.ts
import type { SchemaStore, SchemaRecord } from 'design-schema/store';
import { api } from './api-client';
export class WaypointSchemaStore implements SchemaStore {
async save(record: SchemaRecord) { return api.schemas.create(record); }
async load(id: string) { return api.schemas.get(id); }
async list() { return api.schemas.list(); }
async delete(id: string) { return api.schemas.delete(id); }
}// waypoint-builder-frontend/src/lib/schema-atoms.ts
import { createSchemaAtoms } from 'design-schema/adapters/jotai';
import { WaypointSchemaStore } from './schema-store';
// Provides: specAtom, loadSpecAtom, saveSpecAtom, hasUnsavedChangesAtom
// with built-in 1s debounce autosave — same pattern as the archived workflow-builder-template
export const schemaAtoms = createSchemaAtoms(new WaypointSchemaStore());// Only import the data schemas — no layout or workflow code in the bundle
import { dataSchemas } from 'design-schema/schemas/data';
import { buildSystemPrompt } from 'design-schema/prompts';
const systemPrompt = buildSystemPrompt(dataSchemas, {
theme: 'dark',
designRules: ['always include sample data in state', 'use StatsCard for numeric KPIs'],
});| Preset | Domains included | Typical use case |
|---|---|---|
dashboard |
core + data + feedback + actions | Analytics pages, KPI dashboards |
form |
core + forms + feedback + actions | Data entry, settings pages |
workflow |
core + workflow + ai + actions | Workflow builders, process editors |
full |
All domains | General-purpose, unknown UI type |
Re-exports @json-render/shadcn's layout components: Container, Stack, Grid, Card, Separator, Tabs, Accordion, Dialog, Drawer.
ai-design-system-specific data display:
StatsCard— KPI card with label, value, trend indicator (up/down/neutral), descriptionInteractiveChart— Recharts chart (area / bar / line), bound to state viastatePathDataTable— TanStack table, accepts column definitions +statePathfor row data
StateNode— Workflow state node with status badgesTransitionNode— Workflow transition/edge nodeWorkflowToolbar— Save / undo / redo / execute toolbar
Message— AI conversation message bubble (user / assistant)PromptInput— Chat input with submitCodeBlock— Syntax-highlighted codeChainOfThought— Collapsible reasoning displayArtifact— Rendered output artifact frame
What gets stored in the DB:
interface SchemaRecord {
id: string;
name: string;
description?: string;
spec: Spec; // the json-render Spec object
catalogPreset: string; // e.g. "dashboard" — needed to pick the right registry on load
version: string; // design-schema package version at time of generation
createdAt: string; // ISO timestamp
updatedAt: string;
}version is stored alongside the spec so consumers can detect when a saved spec was generated against an older catalog and handle migration or re-generation.
| Bump | Reason | Impact |
|---|---|---|
| Major | Component name or prop shape renamed/removed | Saved specs may no longer render; system prompts are stale |
| Minor | New schema domain or new component added | Backward-compatible; old specs still render |
| Patch | AI description text improved, Zod refinements | May improve generation quality; no structural change |
Recommendation for consumers: pin the minor version in production ("design-schema": "~1.2.0"). Description text changes in patches can drift AI output even without schema changes.
Consumers can add their own components to any preset without forking the package:
import { dashboardPreset } from 'design-schema/presets/dashboard';
import { z } from 'zod';
import { MyCustomWidget } from '@my-org/components';
const extendedPreset = dashboardPreset.extend({
schemas: {
MyWidget: {
props: z.object({ title: z.string(), dataKey: z.string() }),
description: 'Custom widget for internal KPIs.',
example: { title: 'Pipeline Health', dataKey: 'pipeline' },
},
},
registry: {
MyWidget: ({ props }) => <MyCustomWidget {...props} />,
},
});User types prompt in RefinementPanel
↓
useAIUIBuilder hook (consumer code)
↓
POST /api/generate-ui { prompt, currentSpec? }
↓
waypoint-builder-backend
dashboardPreset.getSystemPrompt() ← design-schema
buildUserPrompt({ prompt }) ← design-schema
streamText(model, system, prompt) ← consumer's AI SDK call
→ stream SpecStream back to frontend
↓
Frontend patches spec into Jotai atom ← consumer's state logic
↓
schemaAtoms.saveSpecAtom (debounced) ← design-schema/adapters/jotai (optional)
→ api.schemas.save(SchemaRecord) ← consumer's API + DB
↓
<Renderer
registry={dashboardPreset.registry} ← design-schema
spec={spec} ← consumer's Jotai atom
/>