Skip to main content

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 TypeDescriptionUse Case
listViewDisplays tabular data with columnsProduct lists, customer lists
detailViewShows detailed view with custom componentsRecord details, dashboards
screenViewGeneric screen with custom componentsCustom layouts, home pages
embeddedViewScreen that can be embedded in other screensReusable components
simpleCrudCombined list and form for CRUD operationsAdmin panels, simple entities
dashboardDashboard with widgetsAnalytics, 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:

PropertyTypeRequiredDescription
keystringYesUnique screen identifier
titlestringYesScreen title displayed in UI
descriptionstringNoScreen description/help text
appstringYesApp key this screen belongs to
routestringYesURL 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 loads
  • loadAsEmptyView - When loading as empty view
  • loadAsUpdateRecord - When loading for record update
{
"onLoad": {
"on": "load",
"actions": [
{
"action": "executeQuery",
"actionProps": {
"query": "loadInitialData"
}
}
]
}
}

Event Handler Actions

Available actions for event handlers:

ActionDescriptionProperties
executeQueryExecute a queryquery, onErrorMessage, onSuccessMessage
controlComponentControl a componentcomponentName, value
navigateToNavigate to URLlink
toastShow toast messagemessage, description, type
openDrawerOpen a drawerscreenKey, width (optional)
closeDrawerClose drawerrefreshData (optional)
refreshDataRefresh screen data-
validateFormValidate form-
clearFormClear 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

PropertyTypeRequiredDescription
screenType"listView"YesMust be exactly "listView"
listQuerystringYesQuery key for loading list data
columnsConfigurationColumnConfiguration[]YesColumn definitions
querySearchFieldsstring[]NoFields 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:

PropertyTypeRequiredDescription
renderAsstringYesColumn render type
fieldstringYesField name for sorting/filtering
labelstringYesColumn header text
valuestringYesTemplate expression for cell value
widthnumberNoColumn width in pixels
fixed"left" | "right" | booleanNoPin column to side
sorterbooleanNoEnable 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 EnumTags with true/false colors
  • For currency: Use Decimal with prefix/suffix
  • For links: Use Text with options.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

PropertyTypeRequiredDescription
screenType"detailView"YesMust be exactly "detailView"
componentsS9ComponentYesSerialized 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

PropertyTypeRequiredDescription
screenType"screenView"YesMust be exactly "screenView"
componentsS9ComponentYesSerialized 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

PropertyTypeRequiredDescription
screenType"embeddedView"YesMust be exactly "embeddedView"
componentsS9ComponentYesSerialized 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

PropertyTypeRequiredDescription
screenType"simpleCrud"YesMust be exactly "simpleCrud"
columnsConfigurationColumnConfiguration[]YesColumn definitions for list
listQuerystringYesQuery for list data (must expose pagination params)
detailQuerystringYesQuery for detail data (must expose id param)
entityKeystringYesEntity key
useRouteForUpdatestringNoRoute for update operations
formFieldsetFormFieldsetYesForm 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:

ParameterRequiredDescriptionUsage
pageYesCurrent page number (zero-indexed)queryParams: {"page": "{{page}}"}
limitYesRecords per pagequeryParams: {"limit": "{{limit}}"}

Recommended additions:

  • filters array for advanced filtering capabilities
  • querySearchFields for text search functionality
  • $sort in 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:

ParameterRequiredDescriptionUsage
idYesRecord identifierbodyParams: {"$where": {"id": "{{id}}"}}

Common patterns:

  • Place id in the $where clause of bodyParams
  • Can be a string template: "id": followed by the id parameter (no quotes around the parameter)
  • Include $withRelated for 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

IssueCauseSolution
Pagination controls missingMissing page/limit paramsAdd page and limit parameters to queryParams
Wrong record loads in detail viewMissing or incorrect id paramEnsure id parameter is in bodyParams
Filters not showingNo filters array in queryAdd filters array to listQuery definition
Search box not appearingNo querySearchFieldsAdd querySearchFields array with field names
Related data missingNo $withRelatedAdd $withRelated array in detailQuery

Important Notes:

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:

PropertyTypeRequiredDescription
fieldstringYesField name
labelstringNoField label
colSizenumberNoColumn size (1-24)
isRequiredbooleanNoRequired field
isReadOnlybooleanNoRead-only field
renderAsstringNoInput type to render
inputTypeOptionsanyNoOptions for input type
rulesFormRule[]NoValidation rules
prefixstringNoField prefix text
valueanyNoDefault value
columnsConfigurationColumnConfiguration[]NoFor grid fields
listQuerystringNoQuery for dropdown data
querySearchFieldsstring[]NoSearch fields for query
showFilterbooleanNoShow filter UI
showCreatebooleanNoShow create button
searchOnClearbooleanNoSearch when cleared

Form Input Types (renderAs)

Available form input types:

TypeDescription
InputText input field
SingleCheckboxSingle checkbox
InputNumberNumeric input
ToggleToggle switch
TimezoneTimezone selector
ColorPickerColor picker
DropdownSingle select dropdown
PasswordPassword input
RadioRadio button group
ObjectKeyValueKey-value pair editor
ValueCodeMirrorCode editor
DateFieldDate picker
FileFieldFile 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 error
  • message - Validation message
  • enum - Allowed values
  • len - Exact length
  • max - Maximum value/length
  • min - Minimum value/length
  • required - Field is required
  • type - Value type (string, number, boolean, etc.)
  • whitespace - Allow whitespace
  • validateTrigger - 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

PropertyTypeRequiredDescription
screenType"dashboard"YesMust be exactly "dashboard"
widgetsSerializedWidget[]YesDashboard widgets
pinToHeaderbooleanYesPin 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 - Use EnumTags instead
  • Currency - Use Decimal with prefix
  • Link - Use Text with options.linkProp
  • Number - Use Decimal

Correct Property Names

  • enumColor not colorMapping or enumColors
  • afterDecimalPoint not decimals or precision

See Also