UI Rendering: Schema-Driven Dynamic Forms
Status: MVP (v2.0.0) | Maturity: Production-Ready | Tests: 45 (31 unit + 9 integration + 5 contract)
Performance: 42ms cache hit, 127ms cache miss | Adapters: 4 (Appsmith, RJSF, ECharts, TanStack)
Cascade Platform generates rich user interfaces from JSON Schema, supporting multiple frameworks and rendering targets without writing UI code.
What is Schema-Driven UI Rendering?
Instead of building forms in React, Vue, or HTML, you define forms in JSON Schema with UI hints. Cascade automatically renders across multiple frameworks:
- Appsmith (primary): Full-featured forms, tables, charts, dashboards
- RJSF (secondary): Lightweight forms, perfect for mobile/simple apps
- ECharts: Data visualization and charts
- TanStack Table: Sortable, filterable data tables
Key benefits:
- ✅ No UI code to maintain
- ✅ Consistent UI across platforms
- ✅ Dynamic forms (show/hide fields based on values)
- ✅ Real-time validation
- ✅ Accessible (WCAG 2.1 AA)
How It Works: CDL → JSON Schema → UI
The Rendering Pipeline
┌─────────────────────────────────────────┐
│ 1. CDL HumanTask Definition │
│ ├── ui: │
│ │ schema: urn:cascade:schema:... │
│ │ target: appsmith │
│ └── assignee, timeout, etc. │
└──────────────┬──────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ 2. JSON Schema Lookup (URN registry) │
│ ├── Fetch schema definition │
│ ├── Resolve $ref references │
│ └── Validate schema structure │
└──────────────┬──────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ 3. Adapter Selection │
│ ├── Appsmith → Rich forms │
│ ├── RJSF → Lightweight │
│ ├── ECharts → Visualizations │
│ └── TanStack → Data tables │
└──────────────┬──────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ 4. Context Interpolation │
│ ├── Inject workflow data │
│ ├── Pre-populate fields │
│ └── Apply conditional visibility │
└──────────────┬──────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ 5. Render in Browser │
│ ├── Display form/chart │
│ ├── Handle user input │
│ └── Validate & submit │
└─────────────────────────────────────────┘JSON Schema Basics
Simple Form Schema
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"title": "Loan Application",
"properties": {
"applicant_name": {
"type": "string",
"title": "Full Name",
"minLength": 1,
"maxLength": 100
},
"email": {
"type": "string",
"title": "Email Address",
"format": "email"
},
"loan_amount": {
"type": "number",
"title": "Loan Amount",
"minimum": 1000,
"maximum": 1000000,
"description": "Amount requested (USD)"
},
"loan_purpose": {
"type": "string",
"title": "Purpose",
"enum": ["home", "auto", "personal", "business"],
"default": "personal"
},
"employment_status": {
"type": "string",
"enum": ["employed", "self_employed", "retired", "student", "unemployed"]
}
},
"required": ["applicant_name", "email", "loan_amount"],
"additionalProperties": false
}Renders as:
┌─────────────────────────────────────┐
│ Loan Application │
├─────────────────────────────────────┤
│ Full Name * │
│ [________________________] │
│ │
│ Email Address * │
│ [________________________] │
│ │
│ Loan Amount * │
│ [________________________] │
│ Amount requested (USD) │
│ │
│ Purpose │
│ [personal ▼] │
│ │
│ Employment Status │
│ [employed ▼] │
│ │
│ [ Submit ] │
└─────────────────────────────────────┘Advanced: UI Hints & Customization
x-ui Properties
JSON Schema extended with x-ui-* properties for UI control:
{
"properties": {
"credit_card": {
"type": "string",
"title": "Credit Card",
"pattern": "^[0-9]{16}$",
"x-ui-mask": "#### #### #### ####",
"x-ui-placeholder": "1234 5678 9012 3456",
"x-ui-type": "password",
"x-ui-help": "Secured with TLS 1.3"
},
"country": {
"type": "string",
"title": "Country",
"x-ui-type": "searchable-select",
"x-ui-options": {
"endpoint": "/api/countries",
"labelField": "name",
"valueField": "code"
}
},
"date_of_birth": {
"type": "string",
"format": "date",
"title": "Date of Birth",
"x-ui-type": "date-picker",
"x-ui-constraints": {
"min": "1900-01-01",
"max": "{{ now | date: 'YYYY-MM-DD' }}"
}
},
"cover_letter": {
"type": "string",
"title": "Cover Letter",
"minLength": 50,
"maxLength": 5000,
"x-ui-type": "textarea",
"x-ui-rows": 8
}
}
}Conditional Visibility
Show/hide fields based on form values:
{
"properties": {
"has_mortgage": {
"type": "boolean",
"title": "Do you have a mortgage?"
},
"mortgage_amount": {
"type": "number",
"title": "Mortgage Balance",
"x-ui-visible-if": "{{ form.has_mortgage === true }}"
},
"mortgage_lender": {
"type": "string",
"title": "Lender Name",
"x-ui-visible-if": "{{ form.has_mortgage === true }}"
},
"employment_type": {
"type": "string",
"enum": ["w2", "1099", "self_employed"]
},
"business_license": {
"type": "string",
"title": "Business License #",
"x-ui-visible-if": "{{ form.employment_type === 'self_employed' }}"
}
}
}Computed Fields
Auto-calculate based on other fields:
{
"properties": {
"monthly_income": {
"type": "number",
"title": "Monthly Income"
},
"tax_rate": {
"type": "number",
"title": "Tax Rate (%)",
"default": 25
},
"net_income": {
"type": "number",
"title": "Net Monthly Income",
"x-ui-compute": "{{ monthly_income * (1 - tax_rate / 100) }}",
"x-ui-readonly": true
}
}
}Rendering Adapters
1. Appsmith (Primary)
Best for: Complex applications, full-featured UI, dashboards
Features:
- Rich forms with 50+ components
- Embedded databases
- Visual workflows
- Drag-and-drop UI builder
- Real-time collaboration
- Built-in tables, charts, maps
CDL Usage:
- name: SubmitLoanApplication
type: HumanTask
ui:
schema: urn:cascade:schema:loan_application
target: appsmith
config:
theme: dark
layout: tabbed
embedded: true
assignee:
role: loan_officer
timeout: 2h
next: ProcessApplicationPerformance:
- Load time: 127ms (cold cache)
- Re-render: 42ms (hot cache)
- Forms with 100 fields:
<200mstotal
Example Schema → Appsmith:
{
"properties": {
"applicant_info": {
"type": "object",
"title": "Applicant Information",
"properties": {
"first_name": { "type": "string", "title": "First Name" },
"last_name": { "type": "string", "title": "Last Name" }
}
},
"financial_info": {
"type": "object",
"title": "Financial Information",
"properties": {
"annual_income": { "type": "number" },
"existing_debts": { "type": "number" }
}
}
},
"x-ui-layout": [
{
"type": "tabs",
"tabs": [
{ "label": "Personal", "schema": "#/properties/applicant_info" },
{ "label": "Financial", "schema": "#/properties/financial_info" }
]
}
]
}2. RJSF (Secondary - Lightweight)
Best for: Simple forms, mobile apps, minimal dependencies
Features:
- Material-UI or Bootstrap themes
- 15+ built-in widgets
- Custom widget support
- Lightweight (
<50KBgzip) - Works offline
CDL Usage:
- name: QuickApproval
type: HumanTask
ui:
schema: urn:cascade:schema:quick_approval_form
target: rjsf
config:
theme: material-ui
compact: true
timeout: 30m
next: ProcessApprovalExample:
{
"title": "Quick Approval",
"properties": {
"approved": {
"type": "boolean",
"title": "Approve?"
},
"notes": {
"type": "string",
"title": "Notes (optional)"
}
},
"uiSchema": {
"approved": { "ui:autofocus": true },
"notes": { "ui:widget": "textarea", "ui:rows": 3 }
}
}3. ECharts (Visualization)
Best for: Data visualization, dashboards, reporting
Features:
- 30+ chart types (line, bar, pie, scatter, heatmap, etc.)
- Real-time data updates
- Interactive legends & tooltips
- Export to PNG/SVG
- Responsive design
CDL Usage:
- name: ReviewSalesMetrics
type: HumanTask
ui:
schema: urn:cascade:schema:sales_dashboard
target: echarts
timeout: 1h
next: ApproveForecastExample Schema (Bar Chart):
{
"title": "Monthly Revenue",
"type": "object",
"properties": {
"chart": {
"x-ui-type": "bar-chart",
"x-ui-data": {
"source": [
["Month", "Revenue", "Target"],
["Jan", 120, 100],
["Feb", 145, 110],
["Mar", 160, 120],
["Apr", 155, 130]
]
},
"x-ui-options": {
"series": [
{ "name": "Revenue", "type": "bar" },
{ "name": "Target", "type": "line" }
],
"xAxis": { "type": "category" },
"yAxis": {}
}
}
}
}4. TanStack Table (Data Grids)
Best for: Large datasets, sorting, filtering
Features:
- Virtual scrolling (1M+ rows)
- Column sorting & filtering
- Pagination
- Export to CSV/Excel
- Cell customization
CDL Usage:
- name: ReviewApplications
type: HumanTask
ui:
schema: urn:cascade:schema:applications_table
target: tanstack
config:
rows_per_page: 50
enable_export: true
timeout: 4h
next: ProcessApplicationsExample Schema:
{
"title": "Loan Applications",
"type": "array",
"items": {
"type": "object",
"properties": {
"id": { "type": "string" },
"applicant": { "type": "string" },
"amount": { "type": "number" },
"status": { "type": "string", "enum": ["pending", "approved", "rejected"] }
}
},
"x-ui-columns": [
{ "id": "applicant", "header": "Applicant Name" },
{ "id": "amount", "header": "Loan Amount", "type": "number", "format": "currency" },
{ "id": "status", "header": "Status", "type": "badge" }
]
}Advanced Features
1. Dynamic Schemas
Load schema based on context:
- name: CollectApplicationDetails
type: HumanTask
ui:
schema: "urn:cascade:schema:application_{{ $.application_type }}"
# Resolves to:
# - urn:cascade:schema:application_mortgage (if type=mortgage)
# - urn:cascade:schema:application_auto (if type=auto)
target: appsmith
timeout: 2h
next: ProcessApplication2. Pre-populated Fields
Inject workflow data into form:
- name: ConfirmApplicationDetails
type: HumanTask
ui:
schema: urn:cascade:schema:application_confirmation
target: appsmith
pre_populate:
applicant_name: "{{ $.verified_name }}"
loan_amount: "{{ $.calculated_amount }}"
employment_status: "{{ $.employment_check.status }}"
timeout: 1h
next: ProcessApplication3. Multi-Step Forms
Break large forms into sections:
{
"title": "Loan Application",
"x-ui-steps": [
{
"step": 1,
"title": "Personal Information",
"schema": {
"type": "object",
"properties": {
"first_name": { "type": "string" },
"last_name": { "type": "string" },
"email": { "type": "string", "format": "email" }
},
"required": ["first_name", "last_name", "email"]
}
},
{
"step": 2,
"title": "Financial Information",
"schema": {
"type": "object",
"properties": {
"annual_income": { "type": "number" },
"existing_debts": { "type": "number" }
},
"required": ["annual_income"]
}
},
{
"step": 3,
"title": "Review & Submit",
"schema": {
"type": "object",
"properties": {
"confirmation": {
"type": "boolean",
"title": "I confirm all information is accurate"
}
},
"required": ["confirmation"]
}
}
]
}Real-World Examples
Example 1: Complex Loan Application
CDL with Appsmith rendering:
workflows:
- name: ProcessLoanApplication
start: CollectPersonalInfo
states:
- name: CollectPersonalInfo
type: HumanTask
ui:
schema: urn:cascade:schema:personal_information
target: appsmith
assignee:
role: loan_officer
timeout: 2h
next: CollectFinancialInfo
- name: CollectFinancialInfo
type: HumanTask
ui:
schema: urn:cascade:schema:financial_information
target: appsmith
pre_populate:
applicant_name: "{{ $.personal_info.full_name }}"
timeout: 2h
next: ReviewAndConfirm
- name: ReviewAndConfirm
type: HumanTask
ui:
schema: urn:cascade:schema:application_review
target: appsmith
pre_populate:
applicant_name: "{{ $.personal_info.full_name }}"
email: "{{ $.personal_info.email }}"
annual_income: "{{ $.financial_info.annual_income }}"
loan_amount: "{{ $.financial_info.loan_amount }}"
timeout: 1h
next: ApplicationComplete
- name: ApplicationComplete
type: Task
resource: urn:cascade:activity:save_application
end: truePersonal Information Schema:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Personal Information",
"type": "object",
"properties": {
"full_name": {
"type": "string",
"title": "Full Name",
"minLength": 1,
"maxLength": 100
},
"email": {
"type": "string",
"title": "Email",
"format": "email"
},
"phone": {
"type": "string",
"title": "Phone Number",
"pattern": "^[0-9\\-\\+\\(\\)\\s]{10,}$"
},
"date_of_birth": {
"type": "string",
"title": "Date of Birth",
"format": "date"
},
"address": {
"type": "object",
"title": "Address",
"properties": {
"street": { "type": "string" },
"city": { "type": "string" },
"state": { "type": "string" },
"zip": { "type": "string", "pattern": "^[0-9]{5}$" },
"country": { "type": "string", "default": "US" }
},
"required": ["street", "city", "state", "zip"]
}
},
"required": ["full_name", "email", "phone", "date_of_birth", "address"],
"additionalProperties": false
}Example 2: Dashboard with ECharts
Sales metrics dashboard:
{
"title": "Sales Dashboard",
"type": "object",
"properties": {
"revenue_chart": {
"x-ui-type": "line-chart",
"x-ui-title": "Monthly Revenue Trend",
"x-ui-data": {
"labels": ["Jan", "Feb", "Mar", "Apr", "May", "Jun"],
"datasets": [
{
"label": "2024 Revenue",
"data": [65, 75, 72, 88, 95, 102]
},
{
"label": "2023 Revenue",
"data": [60, 68, 70, 82, 90, 98]
}
]
}
},
"regional_breakdown": {
"x-ui-type": "pie-chart",
"x-ui-title": "Sales by Region",
"x-ui-data": {
"labels": ["North", "South", "East", "West"],
"datasets": [
{
"data": [25, 20, 35, 20]
}
]
}
},
"conversion_funnel": {
"x-ui-type": "funnel-chart",
"x-ui-title": "Sales Funnel",
"x-ui-data": {
"labels": ["Leads", "Prospects", "Quotes", "Customers"],
"datasets": [
{
"data": [1000, 450, 200, 85]
}
]
}
}
}
}Example 3: Large Data Table with TanStack
- name: ApproveExpenses
type: HumanTask
ui:
schema: urn:cascade:schema:expense_approvals
target: tanstack
config:
selectable: true
bulk_actions:
- action: approve_all
label: "Approve Selected"
- action: reject_all
label: "Reject Selected"
timeout: 4h
next: ProcessApprovalsExpense table schema:
{
"title": "Pending Expense Reports",
"type": "array",
"items": {
"type": "object",
"properties": {
"expense_id": { "type": "string" },
"submitter": { "type": "string" },
"amount": { "type": "number" },
"category": { "type": "string" },
"date": { "type": "string", "format": "date" },
"status": { "type": "string" }
}
},
"x-ui-columns": [
{ "id": "submitter", "header": "Employee", "width": "15%" },
{ "id": "amount", "header": "Amount", "type": "number", "format": "currency", "width": "12%" },
{ "id": "category", "header": "Category", "width": "15%" },
{ "id": "date", "header": "Date", "type": "date", "width": "12%" },
{ "id": "status", "header": "Status", "type": "badge", "width": "10%" }
],
"x-ui-filters": [
{ "id": "category", "type": "multi-select" },
{ "id": "amount", "type": "range" },
{ "id": "date", "type": "date-range" }
]
}Performance Optimization
Caching Strategy
| Scenario | Latency | Strategy |
|---|---|---|
| Cold (new schema) | 127ms | Fetch, parse, render |
| Cache hit (same schema) | 42ms | Use cached parsed schema |
| Context injection | +10ms | Interpolate variables |
| Total (typical) | 52ms | Negligible to users |
Tips for Large Forms
✅ DO:
- Split into multi-step forms
- Use lazy loading for large datasets
- Cache schemas in browser
- Pre-populate from context
- Use virtualization for large tables
❌ DON’T:
- Render 1000+ fields at once
- Fetch all data upfront
- Inline large data
- Disable pagination
Troubleshooting
Schema Validation Errors
Error: Invalid JSON Schema: missing $schema
Solution: Add schema header:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
...
}Fields Not Showing
Symptom: Form renders but fields missing
Causes:
additionalProperties: falsehiding fields- Fields not in
propertiesdict - Schema reference ($ref) not resolved
Debug:
cascade schema inspect urn:cascade:schema:myschemaPerformance Issues
Symptom: Form takes >1s to render
Solutions:
- Check schema size:
cascade schema analyze - Enable caching: Add to config
- Reduce fields: Use multi-step forms
- Profile:
cascade trace ui-render
Best Practices
✅ DO:
- Use semantic property names
- Add descriptions and defaults
- Include validation rules (min, max, pattern)
- Pre-populate when possible
- Use computed fields for derived values
- Enable field-level validation
❌ DON’T:
- Store large data in schemas
- Use overly complex conditionals
- Nest more than 3 levels
- Load entire datasets upfront
- Omit accessibility attributes
Next Steps
Ready to build forms? → First Workflow Tutorial
Need JSON Schema help? → Schema Reference
Want advanced customization? → UI Customization Guide
Performance troubleshooting? → Performance Guide
Updated: October 29, 2025
Version: 2.0
JSON Schema Version: Draft 7
Production-Ready: Yes