Screen Schema Reference
This document provides the complete, canonical reference for Stack9 screen definitions. It documents all screen types, properties, and configurations based on the official runtype validators in packages/stack9-sdk/src/models/core/S9Screen.ts.
Screen Structure Overview
Every Stack9 screen follows this basic structure:
{
"head": {
// Screen metadata (required for all screens)
},
"screenType": "...", // Screen type identifier
"onLoad": {...}, // Optional onLoad event handler
"queries": [...], // Optional query definitions
"module": "...", // Optional module reference
// Type-specific properties...
}
Screen Types
Stack9 supports exactly 6 screen types (note the camelCase naming):
| Screen Type | Description | Use Case |
|---|---|---|
listView | Displays tabular data with columns | Product lists, customer lists |
detailView | Shows detailed view with custom components | Record details, dashboards |
screenView | Generic screen with custom components | Custom layouts, home pages |
embeddedView | Screen that can be embedded in other screens | Reusable components |
simpleCrud | Combined list and form for CRUD operations | Admin panels, simple entities |
dashboard | Dashboard with widgets | Analytics, overview screens |
Important: Screen types use camelCase (e.g., listView not ListView, simpleCrud not SimpleCrud)
Common Screen Properties
Screen Head (S9ScreenHead)
All screens require a head object with these properties:
| Property | Type | Required | Description |
|---|---|---|---|
key | string | Yes | Unique screen identifier |
title | string | Yes | Screen title displayed in UI |
description | string | No | Screen description/help text |
app | string | Yes | App key this screen belongs to |
route | string | Yes | URL route for the screen |
Example:
{
"head": {
"key": "customer_list",
"title": "Customers",
"description": "Manage customer records",
"app": "crm",
"route": "customers"
}
}
Event Handlers
Screens can define event handlers for lifecycle events and user interactions.
Screen Event Handlers (onLoad)
Available screen events:
load- When screen loadsloadAsEmptyView- When loading as empty viewloadAsUpdateRecord- When loading for record update
{
"onLoad": {
"on": "load",
"actions": [
{
"action": "executeQuery",
"actionProps": {
"query": "loadInitialData"
}
}
]
}
}
Event Handler Actions
Available actions for event handlers:
| Action | Description | Properties |
|---|---|---|
executeQuery | Execute a query | query, onErrorMessage, onSuccessMessage |
controlComponent | Control a component | componentName, value |
navigateTo | Navigate to URL | link |
toast | Show toast message | message, description, type |
openDrawer | Open a drawer | screenKey, width (optional) |
closeDrawer | Close drawer | refreshData (optional) |
refreshData | Refresh screen data | - |
validateForm | Validate form | - |
clearForm | Clear form | - |
Queries
Screens can define queries to load data:
{
"queries": [
{
"name": "loadCustomerData",
"queryKey": "getcustomer",
"userParams": {
"customerId": "{{params.id}}"
}
}
]
}
Screen Type: listView
Displays data in a tabular format with columns.
Properties
| Property | Type | Required | Description |
|---|---|---|---|
screenType | "listView" | Yes | Must be exactly "listView" |
listQuery | string | Yes | Query key for loading list data |
columnsConfiguration | ColumnConfiguration[] | Yes | Column definitions |
querySearchFields | string[] | No | Fields to search in |
Column Configuration
Each column in columnsConfiguration must specify a renderAs type and associated properties.
Base Column Properties
All column types share these base properties:
| Property | Type | Required | Description |
|---|---|---|---|
renderAs | string | Yes | Column render type |
field | string | Yes | Field name for sorting/filtering |
label | string | Yes | Column header text |
value | string | Yes | Template expression for cell value |
width | number | No | Column width in pixels |
fixed | "left" | "right" | boolean | No | Pin column to side |
sorter | boolean | No | Enable sorting for this column |
Column Render Types
Text
Basic text display with optional link.
{
"renderAs": "Text",
"field": "name",
"label": "Name",
"value": "{{name}}",
"options": {
"linkProp": "/customer/{{id}}", // Optional link
"screenKey": "customer_detail" // Optional screen key
}
}
Image
Display image with dimensions.
{
"renderAs": "Image",
"field": "avatar",
"label": "Photo",
"value": "{{avatar_url}}",
"options": {
"width": 50,
"height": 50,
"linkProp": "/profile/{{id}}", // Optional
"alt": "User avatar" // Alt text (recommended)
}
}
StockValue
Display stock/inventory values.
{
"renderAs": "StockValue",
"field": "stock",
"label": "Stock",
"value": "{{stock_quantity}}"
}
Decimal
Format decimal numbers with prefix/suffix.
{
"renderAs": "Decimal",
"field": "price",
"label": "Price",
"value": "{{price}}",
"options": {
"prefix": "$",
"suffix": " USD",
"afterDecimalPoint": 2
}
}
EnumTags
Display as colored tags/badges.
{
"renderAs": "EnumTags",
"field": "status",
"label": "Status",
"value": "{{status}}",
"options": {
"enumColor": {
"active": "green",
"pending": "orange",
"inactive": "red"
}
}
}
HumanizedTime
Display time in human-readable format.
{
"renderAs": "HumanizedTime",
"field": "last_login",
"label": "Last Login",
"value": "{{last_login_at}}"
}
StringArray
Display array of strings from related entity.
{
"renderAs": "StringArray",
"field": "tags",
"label": "Tags",
"value": "{{tags}}",
"entity": "tag",
"entityFields": ["name", "category"]
}
ElasticStringArray
Display elastic/dynamic string array.
{
"renderAs": "ElasticStringArray",
"field": "keywords",
"label": "Keywords",
"value": "{{keywords}}",
"entity": "keyword",
"entityField": "value"
}
WorkflowResult
Display workflow execution result.
{
"renderAs": "WorkflowResult",
"field": "workflow_status",
"label": "Workflow Status",
"value": "{{workflow_result}}"
}
Date
Format date/datetime values.
{
"renderAs": "Date",
"field": "created_at",
"label": "Created",
"value": "{{created_at}}",
"options": {
"noTime": true // Hide time component
}
}
CustomComponent
Render custom React component.
{
"renderAs": "CustomComponent",
"field": "custom",
"label": "Custom",
"value": "{{data}}",
"options": {
"customComponentName": "MyCustomRenderer",
"props": {
"additionalProp": "value"
}
}
}
Note: There is no "Boolean", "Currency", or "Link" renderAs type. Use:
- For boolean: Use
EnumTagswith true/false colors - For currency: Use
Decimalwith prefix/suffix - For links: Use
Textwithoptions.linkProp
Complete listView Example
{
"head": {
"key": "product_list",
"title": "Products",
"app": "inventory",
"route": "products"
},
"screenType": "listView",
"listQuery": "getproductlist",
"querySearchFields": ["name", "sku", "description"],
"columnsConfiguration": [
{
"renderAs": "Text",
"field": "sku",
"label": "SKU",
"value": "{{sku}}",
"options": {
"linkProp": "/inventory/product/{{id}}"
}
},
{
"renderAs": "Text",
"field": "name",
"label": "Product Name",
"value": "{{name}}"
},
{
"renderAs": "Decimal",
"field": "price",
"label": "Price",
"value": "{{price}}",
"options": {
"prefix": "$",
"afterDecimalPoint": 2
}
},
{
"renderAs": "EnumTags",
"field": "status",
"label": "Status",
"value": "{{status}}",
"options": {
"enumColor": {
"active": "green",
"out_of_stock": "red"
}
}
}
]
}
Screen Type: detailView
Displays detailed view with custom component layout.
Properties
| Property | Type | Required | Description |
|---|---|---|---|
screenType | "detailView" | Yes | Must be exactly "detailView" |
components | S9Component | Yes | Serialized component tree |
Example
{
"head": {
"key": "customer_detail",
"title": "Customer Details",
"app": "crm",
"route": "customer/:id"
},
"screenType": "detailView",
"components": {
// Serialized component tree
}
}
Screen Type: screenView
Generic screen with custom component layout.
Properties
| Property | Type | Required | Description |
|---|---|---|---|
screenType | "screenView" | Yes | Must be exactly "screenView" |
components | S9Component | Yes | Serialized component tree |
Example
{
"head": {
"key": "home_screen",
"title": "Home",
"app": "main",
"route": "home"
},
"screenType": "screenView",
"components": {
// Serialized component tree
}
}
Screen Type: embeddedView
Screen that can be embedded within other screens.
Properties
| Property | Type | Required | Description |
|---|---|---|---|
screenType | "embeddedView" | Yes | Must be exactly "embeddedView" |
components | S9Component | Yes | Serialized component tree |
Example
{
"head": {
"key": "customer_summary",
"title": "Customer Summary",
"app": "crm",
"route": "embedded/customer-summary"
},
"screenType": "embeddedView",
"components": {
// Serialized component tree
}
}
Screen Type: simpleCrud
Combined list and form view for CRUD operations.
Properties
| Property | Type | Required | Description |
|---|---|---|---|
screenType | "simpleCrud" | Yes | Must be exactly "simpleCrud" |
columnsConfiguration | ColumnConfiguration[] | Yes | Column definitions for list |
listQuery | string | Yes | Query for list data (must expose pagination params) |
detailQuery | string | Yes | Query for detail data (must expose id param) |
entityKey | string | Yes | Entity key |
useRouteForUpdate | string | No | Route for update operations |
formFieldset | FormFieldset | Yes | Form field configuration |
Query Requirements
The simpleCrud screen type has specific requirements for its queries to enable the Stack9 framework's automatic features.
List Query Requirements
The listQuery MUST expose pagination parameters for the framework to handle automatic pagination:
| Parameter | Required | Description | Usage |
|---|---|---|---|
page | Yes | Current page number (zero-indexed) | queryParams: {"page": "{{page}}"} |
limit | Yes | Records per page | queryParams: {"limit": "{{limit}}"} |
Recommended additions:
filtersarray for advanced filtering capabilitiesquerySearchFieldsfor text search functionality$sortin bodyParams for default sorting
Example listQuery configuration:
{
"key": "getproductlist",
"name": "getProductList",
"connector": "stack9_api",
"queryTemplate": {
"method": "post",
"path": "/product/search",
"bodyParams": {
"$where": { "_is_deleted": false },
"$sort": { "id": "desc" }
},
"queryParams": {
"page": "{{page}}", // REQUIRED: Framework will inject page number
"limit": "{{limit}}" // REQUIRED: Framework will inject page size
}
},
"querySearchFields": ["name", "description"], // Enables text search
"filters": [ // Enables advanced filtering
{
"name": "Status",
"key": "status",
"typeQueryFilter": "array",
"field": "status",
"typeFilter": "MultiDropDown",
"sequence": 1,
"dataSource": {
"type": "static",
"options": [
{ "label": "Active", "value": "active" },
{ "label": "Inactive", "value": "inactive" }
]
}
}
]
}
Detail Query Requirements
The detailQuery MUST expose an id parameter for the framework to load individual records:
| Parameter | Required | Description | Usage |
|---|---|---|---|
id | Yes | Record identifier | bodyParams: {"$where": {"id": "{{id}}"}} |
Common patterns:
- Place
idin the$whereclause of bodyParams - Can be a string template:
"id":followed by the id parameter (no quotes around the parameter) - Include
$withRelatedfor related entities
Example detailQuery configuration:
{
"key": "getproductbyid",
"name": "getProductById",
"connector": "stack9_api",
"queryTemplate": {
"method": "post",
"path": "/product/find",
"bodyParams": {
"$where": {
"_is_deleted": false,
"id": "{{id}}" // REQUIRED: Framework will inject record ID
},
"$withRelated": ["category", "supplier"] // Load related data
}
}
}
Alternative pattern using string template:
{
"queryTemplate": {
"bodyParams": "{\"$where\": {\"_is_deleted\": false, \"id\": \" + id + \"}}"
}
}
Common Query Issues and Solutions
| Issue | Cause | Solution |
|---|---|---|
| Pagination controls missing | Missing page/limit params | Add page and limit parameters to queryParams |
| Wrong record loads in detail view | Missing or incorrect id param | Ensure id parameter is in bodyParams |
| Filters not showing | No filters array in query | Add filters array to listQuery definition |
| Search box not appearing | No querySearchFields | Add querySearchFields array with field names |
| Related data missing | No $withRelated | Add $withRelated array in detailQuery |
Important Notes:
- The framework automatically injects
page,limit, andidvalues - Don't hardcode these values; use template syntax with double curly braces
- For filters reference, see Query Library Schema Reference - Filter Schema
FormFieldset Structure
{
"formFieldset": {
"sections": {
"basic": {
"fields": ["name", "email", "phone"],
"expanded": true
},
"advanced": {
"fields": ["settings", "preferences"],
"expanded": false
}
},
"create": [...], // FormConfiguration array for create
"update": [...], // FormConfiguration array for update
"updateUsesCreateFields": false // Use create fields for update
}
}
Form Configuration
Each field in the form has these properties:
| Property | Type | Required | Description |
|---|---|---|---|
field | string | Yes | Field name |
label | string | No | Field label |
colSize | number | No | Column size (1-24) |
isRequired | boolean | No | Required field |
isReadOnly | boolean | No | Read-only field |
renderAs | string | No | Input type to render |
inputTypeOptions | any | No | Options for input type |
rules | FormRule[] | No | Validation rules |
prefix | string | No | Field prefix text |
value | any | No | Default value |
columnsConfiguration | ColumnConfiguration[] | No | For grid fields |
listQuery | string | No | Query for dropdown data |
querySearchFields | string[] | No | Search fields for query |
showFilter | boolean | No | Show filter UI |
showCreate | boolean | No | Show create button |
searchOnClear | boolean | No | Search when cleared |
Form Input Types (renderAs)
Available form input types:
| Type | Description |
|---|---|
Input | Text input field |
SingleCheckbox | Single checkbox |
InputNumber | Numeric input |
Toggle | Toggle switch |
Timezone | Timezone selector |
ColorPicker | Color picker |
Dropdown | Single select dropdown |
Password | Password input |
Radio | Radio button group |
ObjectKeyValue | Key-value pair editor |
ValueCodeMirror | Code editor |
DateField | Date picker |
FileField | File upload |
Validation Rules
Form fields can have validation rules:
{
"rules": [
{
"required": true,
"message": "This field is required",
"type": "string",
"min": 3,
"max": 50,
"whitespace": true
}
]
}
Rule properties:
warningOnly- Show warning instead of errormessage- Validation messageenum- Allowed valueslen- Exact lengthmax- Maximum value/lengthmin- Minimum value/lengthrequired- Field is requiredtype- Value type (string, number, boolean, etc.)whitespace- Allow whitespacevalidateTrigger- When to validate
Complete simpleCrud Example
{
"head": {
"key": "product_crud",
"title": "Product Management",
"app": "inventory",
"route": "products/manage"
},
"screenType": "simpleCrud",
"entityKey": "product",
"listQuery": "getproductlist", // Must expose page & limit params
"detailQuery": "getproduct", // Must expose id param
"columnsConfiguration": [
{
"renderAs": "Text",
"field": "name",
"label": "Product Name",
"value": "{{name}}"
},
{
"renderAs": "Decimal",
"field": "price",
"label": "Price",
"value": "{{price}}",
"options": {
"prefix": "$"
}
}
],
"formFieldset": {
"sections": {
"basic": {
"fields": ["name", "sku", "price"],
"expanded": true
}
},
"create": [
{
"field": "name",
"label": "Product Name",
"isRequired": true,
"renderAs": "Input",
"rules": [
{
"required": true,
"message": "Product name is required"
}
]
},
{
"field": "price",
"label": "Price",
"renderAs": "InputNumber",
"inputTypeOptions": {
"min": 0,
"precision": 2
}
}
],
"update": [],
"updateUsesCreateFields": true
}
}
Screen Type: dashboard
Dashboard screen with widgets.
Properties
| Property | Type | Required | Description |
|---|---|---|---|
screenType | "dashboard" | Yes | Must be exactly "dashboard" |
widgets | SerializedWidget[] | Yes | Dashboard widgets |
pinToHeader | boolean | Yes | Pin dashboard to header |
Example
{
"head": {
"key": "sales_dashboard",
"title": "Sales Dashboard",
"app": "analytics",
"route": "dashboard"
},
"screenType": "dashboard",
"widgets": [
// Widget configurations
],
"pinToHeader": false
}
Complete SimpleCrud Example with Queries
Here's a comprehensive example showing both the screen definition and its required queries:
Screen Definition
{
"head": {
"key": "customer_management",
"title": "Customer Management",
"app": "crm",
"route": "customers"
},
"screenType": "simpleCrud",
"entityKey": "customer",
"listQuery": "getcustomerlist",
"detailQuery": "getcustomerbyid",
"columnsConfiguration": [
{
"renderAs": "Text",
"field": "name",
"label": "Customer Name",
"value": "{{name}}"
},
{
"renderAs": "Text",
"field": "email",
"label": "Email",
"value": "{{email}}"
},
{
"renderAs": "EnumTags",
"field": "status",
"label": "Status",
"value": "{{status}}",
"options": {
"enumColor": {
"active": "green",
"inactive": "red"
}
}
}
],
"formFieldset": {
"create": [
{
"field": "name",
"label": "Customer Name",
"isRequired": true,
"renderAs": "Input"
},
{
"field": "email",
"label": "Email",
"isRequired": true,
"renderAs": "Input",
"rules": [{
"required": true,
"type": "email",
"message": "Please enter a valid email"
}]
},
{
"field": "status",
"label": "Status",
"renderAs": "Dropdown",
"listQuery": "getstatuslist"
}
],
"updateUsesCreateFields": true
}
}
List Query (getcustomerlist.json)
{
"key": "getcustomerlist",
"name": "getCustomerList",
"connector": "stack9_api",
"queryTemplate": {
"method": "post",
"path": "/customer/search",
"bodyParams": {
"$select": ["id", "name", "email", "status", "created_at"],
"$where": { "_is_deleted": false },
"$sort": { "created_at": "desc" }
},
"queryParams": {
"page": "{{page}}", // REQUIRED for pagination
"limit": "{{limit}}" // REQUIRED for pagination
}
},
"querySearchFields": ["name", "email"],
"filters": [
{
"name": "Status",
"key": "status",
"typeQueryFilter": "array",
"field": "status",
"typeFilter": "MultiDropDown",
"sequence": 1,
"dataSource": {
"type": "static",
"options": [
{ "label": "Active", "value": "active" },
{ "label": "Inactive", "value": "inactive" }
]
}
},
{
"name": "Created Date",
"key": "created_at",
"typeQueryFilter": "between",
"field": "created_at",
"typeFilter": "DateRangeValue",
"sequence": 2
}
],
"userParams": {
"page": "0",
"limit": "10"
}
}
Detail Query (getcustomerbyid.json)
{
"key": "getcustomerbyid",
"name": "getCustomerById",
"connector": "stack9_api",
"queryTemplate": {
"method": "post",
"path": "/customer/find",
"bodyParams": {
"$select": ["*"],
"$where": {
"_is_deleted": false,
"id": "{{id}}" // REQUIRED for record loading
},
"$withRelated": ["orders", "addresses"]
}
},
"userParams": {
"id": "1" // Default for testing
}
}
Important Notes
Screen Type Naming
- Always use camelCase for screen types
- ✅ Correct:
listView,detailView,simpleCrud - ❌ Wrong:
ListView,DetailView,SimpleCrud
Non-Existent Types
The following are NOT valid screen types or properties:
drawer- Not a screen type (use embeddedView or screenView)modal- Not a screen type (use embeddedView or screenView)masterDetailView- Not a screen type (build with listView + detailView)splitRatio- Not a valid property
Column Render Types
The following are NOT valid renderAs values:
Boolean- UseEnumTagsinsteadCurrency- UseDecimalwith prefixLink- UseTextwith options.linkPropNumber- UseDecimal
Correct Property Names
enumColornotcolorMappingorenumColorsafterDecimalPointnotdecimalsorprecision
See Also
- Screens Concept Guide - Overview and practical examples
- Building a CRUD Application - Step-by-step CRUD tutorial
- Master-Detail Screens - Building complex screen layouts