A complete Server-Driven UI platform built from scratch — custom DSL, compiler pipeline, BFF server, and React renderer. The demo domain is an e-commerce storefront.
Live Demo: https://shopui.al-ex.ai
.sdui files Compiler Schema Registry
┌──────────┐ ┌─────────────────┐ ┌─────────────────┐
│ home │───▶│ Lexer → Parser │───▶│ JSON schemas │
│ cart │ │ → AST → CodeGen │ │ (versioned) │
│ checkout │ └─────────────────┘ └────────┬─────────┘
└──────────┘ │
▼
┌───────────────────────────────────────────────────────────┐
│ BFF Server (Express) │
│ │
│ GET /api/screens/:id ──▶ Assembler │
│ • Loads compiled schema from registry │
│ • Fetches data from mock microservices │
│ • Resolves {{data.bindings}} with live values │
│ • Evaluates conditionals & loops │
│ • Returns ready-to-render JSON │
│ │
│ Middleware: Zod validation, rate limiting, CORS, │
│ request logging, global error handler │
│ Mock Services: products, cart, orders, users │
│ Cache: in-memory with TTL │
│ Versioning: v1 / v2 schema negotiation │
└──────────────────────┬────────────────────────────────────┘
│ JSON
▼
┌───────────────────────────────────────────────────────────┐
│ React Client (Vite + MUI) │
│ │
│ SDUIRenderer ──▶ ComponentRegistry ──▶ React Components │
│ • Recursive tree walker │
│ • Maps type strings to MUI components │
│ • ActionHandler: navigate, api_call, set_state │
│ • Error boundary with graceful fallback │
│ • Skeleton loading states │
│ • Client-side schema cache │
│ • Responsive design (mobile-first) │
│ • Dark/light theme toggle │
│ • Interactive feature tour │
└───────────────────────────────────────────────────────────┘
| Layer | Technology |
|---|---|
| Monorepo | pnpm workspaces |
| Compiler | TypeScript (custom lexer + parser) |
| BFF Server | Node + Express + TypeScript |
| Client | React 19 + Vite + MUI + TypeScript |
| Validation | Zod v4 (request schemas) |
| Testing | Vitest (80 tests — compiler + server) |
sdui-platform/
├── screens/ # DSL source files
│ ├── home.sdui
│ ├── product-detail.sdui
│ ├── cart.sdui
│ └── checkout.sdui
├── packages/
│ ├── schema/ # Shared SDUI type definitions
│ │ └── src/types.ts # SDUINode, SDUIAction, SDUIScreen, SDUILayout
│ ├── compiler/ # DSL → JSON compiler pipeline
│ │ └── src/
│ │ ├── lexer.ts # Tokenizer (keywords, literals, operators)
│ │ ├── parser.ts # Recursive descent → AST
│ │ ├── ast.ts # AST node type definitions
│ │ ├── codegen.ts # AST → JSON schema
│ │ ├── index.ts # compile() entry point
│ │ └── __tests__/ # 50 unit tests (lexer, parser, codegen)
│ ├── server/ # BFF server
│ │ └── src/
│ │ ├── app.ts # Express setup + middleware stack
│ │ ├── index.ts # Server entry point (listen)
│ │ ├── assembler.ts # Schema + data → personalized response
│ │ ├── cache.ts # In-memory TTL cache
│ │ ├── middleware/ # Validation, error handling, logging
│ │ │ ├── validate.ts # Zod schema validation
│ │ │ ├── errorHandler.ts # Global error handler + AppError
│ │ │ ├── requestLogger.ts # Request timing/logging
│ │ │ └── capabilities.ts # Client capability negotiation
│ │ ├── routes/ # cart, screens, orders, ai
│ │ ├── services/ # Mock microservices (products, cart, etc.)
│ │ └── __tests__/ # 30 tests (routes + assembler)
│ └── client/ # React SDUI renderer
│ └── src/
│ ├── App.tsx # Routing, theme, NavBar, feature tour
│ ├── renderer/
│ │ ├── SDUIRenderer.tsx # Recursive component renderer
│ │ ├── ComponentRegistry.ts # Type → React component map
│ │ └── ActionHandler.ts # Action interpreter
│ ├── components/ # MUI component implementations
│ │ ├── SDUI{Text,Button,Card,Image,Input,...}.tsx
│ │ ├── SDUISkeleton.tsx # Loading placeholders
│ │ ├── SDUIErrorBoundary.tsx # Render error recovery
│ │ └── FeatureTour.tsx # Interactive guided tour
│ ├── contexts/
│ │ └── NotificationContext.tsx # Toast notification system
│ └── hooks/
│ ├── useScreen.ts # Fetch + cache screen schema
│ └── useSDUIState.ts # Local form state bindings
The custom .sdui DSL compiles to JSON schemas consumed by the renderer:
screen "Home" {
layout: column, spacing: 20, padding: 24
// Hero banner
container {
padding: 24, background: "primary.main"
text { content: "Welcome to ShopUI", variant: "h3" }
}
// Product grid with data binding
grid { columns: 3, gap: 16 }
each products as product {
card {
image { src: product.image, alt: product.name }
text { content: product.name, variant: "body1" }
button {
label: "Add to Cart",
action: api_call("/api/cart/add", "POST", { productId: product.id })
}
}
}
// Conditional rendering
if cart.count > 0 {
button { label: "View Cart", action: navigate("/cart") }
}
}
Supported constructs: components with props, nested children, each loops with data binding, if conditionals with comparison operators, navigate/api_call/set_state actions, layout properties, {{data.ref}} template bindings.
Source (.sdui)
│
▼
┌─────────┐ Tokens ┌────────┐ AST ┌─────────┐ JSON Schema
│ Lexer │────────────▶│ Parser │───────────▶│ CodeGen │──────────────▶
└─────────┘ └────────┘ └─────────┘
- Lexer — Tokenizes source into keywords (
screen,if,each,as), literals (strings, numbers, booleans), operators (>,==,!=), identifiers, and symbols - Parser — Recursive descent parser producing an AST with
ScreenNode,ComponentNode,ConditionalNode,LoopNode - CodeGen — Transforms AST into the JSON schema format, resolving data references to
{{binding}}syntax
# Install dependencies
pnpm install
# Build shared packages & compile DSL
pnpm build
pnpm compile:screens
# Start both server and client
pnpm dev
# Or start individually:
pnpm dev:server # http://localhost:3004
pnpm dev:client # http://localhost:5174# All tests (80 total)
pnpm test
# Compiler unit tests (50 tests: lexer, parser, codegen)
pnpm --filter @sdui/compiler test
# Server tests (30 tests: routes + assembler)
pnpm --filter @sdui/server testToggle between v1 and v2 schemas via the version chip in the NavBar. The server negotiates the schema version and adapts its response format.
Components declare actions in the DSL that the client interprets at runtime:
navigate("/path")— Client-side routing via React Routerapi_call("/endpoint", "POST", { body })— API calls with automatic screen refresh and toast feedbackset_state("key", value)— Local state mutations for form bindings
Success/error feedback is surfaced via a toast notification system (MUI Snackbar) wired into the action handler.
The SDUIErrorBoundary wraps all rendered components. If a bad schema causes a render crash, users see a friendly error with a retry button instead of a white screen.
All components are mobile-first responsive:
- Grid columns collapse: 3 → 2 → 1 on smaller screens
- NavBar adapts: full text → icon-only on mobile
- Touch-friendly spacing and sizing
Toggle via the theme icon in the NavBar. Persisted in localStorage.
First-time visitors get a spotlight tour highlighting key UI features. Reopen anytime via the ? icon.
Describe a screen in plain English → AI generates DSL → compiler validates → renderer previews live. Demonstrates AI as a developer velocity multiplier. The compiler acts as a safety net — AI hallucinations are caught at compile time, not runtime.
| Endpoint | Description |
|---|---|
GET /api/screens/:id |
Fetch assembled screen schema |
GET /api/products |
Product catalog |
GET /api/cart |
Current cart state |
POST /api/cart/add |
Add item to cart |
POST /api/cart/remove |
Remove item from cart |
POST /api/checkout |
Place order |
POST /api/ai/generate |
AI screen generation (Gemini) |
- Custom DSL over JSON authoring — More readable, catches errors at compile time, enables tooling
- BFF assembles final schema — Client stays thin; server handles data fetching, personalization, and conditional logic
- Zod validation on every route — Request bodies, params, and queries validated with structured error responses
- Global error handler + AppError — Consistent
{ error: { code, message } }format across all endpoints - App/server split —
app.tsexports the Express app,index.tscalls listen — enables supertest integration tests - No external tour library — Built spotlight tour with pure CSS box-shadow cutout technique
- Class component for error boundary — React requires class components for
getDerivedStateFromError - MUI for components — Familiar, accessible, themeable — focus stays on the SDUI architecture, not styling
- AI generates DSL, not JSON — Compiler validates AI output the same way it validates human-authored code
- Gemini via REST — Simple HTTP call, no SDK dependency; easily swappable for Claude, OpenAI, etc.