Screens
Screens define the user interface for your Stack9 application. A screen is a JSON configuration that tells Stack9 how to render data grids, forms, and custom views without writing frontend code.
What is a Screen?
A screen configuration defines:
- Layout: List views, detail views, drawers, or modals
- Data source: Which query to execute
- Columns: What fields to display and how
- Actions: Buttons and operations available
- Permissions: Who can view and interact
When you define a screen, Stack9 automatically:
- ✅ Renders the UI with proper styling
- ✅ Handles data fetching and pagination
- ✅ Manages sorting and filtering
- ✅ Handles form validation
- ✅ Manages navigation
- ✅ Applies role-based permissions
Screen Types
Stack9 supports four primary screen types:
1. listView - Data Grids
Display tabular data with sorting, filtering, and pagination.
Use cases: Customer lists, order history, search results
2. detailView - Record Forms
Display and edit a single record with form fields.
Use cases: Customer profile, order details, product editor
3. drawer - Slide-Out Panels
Side panel for quick create/edit operations without leaving the current page.
Use cases: Quick add customer, update status, add comment
4. modal - Pop-Up Forms
Modal dialog for focused interactions.
Use cases: Confirmations, quick edits, wizards
Screen File Structure
Screens are defined in src/screens/{screen_name}.json:
{
"head": {
// Screen metadata
},
"screenType": "listView | detailView | drawer | modal",
"listQuery": "query_key", // For listView
"detailQuery": "query_key", // For detailView
"columnsConfiguration": [], // Column definitions
"actions": [], // Custom actions
"permissions": {} // Access control
}
List View Screens
List views display tabular data with powerful filtering and sorting capabilities.
Basic List View Example
{
"head": {
"title": "Customer List",
"key": "customer_list",
"route": "customer-list",
"app": "crm",
"description": "Browse all customers"
},
"screenType": "listView",
"listQuery": "getactivecustomerlist",
"columnsConfiguration": [
{
"field": "crn",
"label": "CRN",
"value": "{{crn}}",
"renderAs": "Text",
"options": {
"linkProp": "/crm/customer-profile/{{id}}"
}
},
{
"label": "Full Name",
"field": "fullname",
"renderAs": "Text",
"value": "{{[name, last_name].filter(Boolean).join(' ')}}"
},
{
"field": "email_address",
"label": "Email",
"value": "{{email_address}}",
"renderAs": "Text"
},
{
"field": "is_active",
"label": "Active",
"value": "{{is_active}}",
"renderAs": "Boolean"
}
]
}
Head Configuration
| Property | Type | Required | Description |
|---|---|---|---|
title | string | Yes | Screen title shown in UI |
key | string | Yes | Unique identifier |
route | string | Yes | URL route (e.g., /crm/customer-list) |
app | string | Yes | App key this screen belongs to |
description | string | No | Help text |
icon | string | No | Icon component name |
Column Configuration
Columns define how each field is displayed in the grid:
{
"field": "crn", // Field name (for sorting/filtering)
"label": "CRN", // Column header
"value": "{{crn}}", // Template for cell value
"renderAs": "Text", // Render type
"options": { // Render options
"linkProp": "/customer/{{id}}"
}
}
Render Types
Text - Plain text display
{
"field": "name",
"label": "Name",
"value": "{{name}}",
"renderAs": "Text"
}
Date - Formatted date/datetime
{
"field": "created_at",
"label": "Created",
"value": "{{created_at}}",
"renderAs": "Date",
"options": {
"noTime": true, // Hide time component
"format": "DD/MM/YYYY" // Custom format
}
}
Boolean - Checkmark or badge
{
"field": "is_active",
"label": "Active",
"value": "{{is_active}}",
"renderAs": "Boolean"
}
EnumTags - Colored tags/badges
{
"field": "status",
"label": "Status",
"value": "{{status}}",
"renderAs": "EnumTags",
"options": {
"enumColor": {
"pending": "orange",
"approved": "green",
"rejected": "red"
}
}
}
Currency - Formatted currency
{
"field": "total_amount",
"label": "Total",
"value": "{{total_amount}}",
"renderAs": "Currency",
"options": {
"currency": "AUD",
"decimals": 2
}
}
Link - Clickable link
{
"field": "crn",
"label": "CRN",
"value": "{{crn}}",
"renderAs": "Text",
"options": {
"linkProp": "/customer/{{id}}"
}
}
Image - Image thumbnail
{
"field": "avatar",
"label": "Photo",
"value": "{{avatar_url}}",
"renderAs": "Image",
"options": {
"width": 50,
"height": 50
}
}
Template Expressions
Use template expressions in the value field to transform data:
Concatenate Fields
{
"label": "Full Name",
"value": "{{[name, last_name].filter(Boolean).join(' ')}}"
}
Conditional Display
{
"label": "Location",
"value": "{{suburb ? suburb + ', ' + state : 'N/A'}}"
}
Array Mapping
{
"label": "Attention Flags",
"value": "{{customer_attention_flags.map(item => item.attention_flag.flag)}}"
}
Related Entity Fields
{
"label": "Sales Channel",
"value": "{{sales_channel.name}}"
}
Real-World List View Example
From a production Stack9 app:
{
"head": {
"title": "Customer list",
"key": "customer_list",
"route": "customer-list",
"app": "crm"
},
"screenType": "listView",
"listQuery": "getactivecustomerlist",
"columnsConfiguration": [
{
"field": "crn",
"label": "CRN",
"value": "{{crn}}",
"renderAs": "Text",
"options": {
"linkProp": "/crm/customer-profile/{{id}}"
}
},
{
"label": "Fullname",
"field": "fullname",
"renderAs": "Text",
"value": "{{[name, last_name].filter(Boolean).join(' ')}}"
},
{
"field": "phone",
"label": "Phone",
"value": "{{phone}}",
"renderAs": "Text"
},
{
"field": "dob",
"label": "DOB",
"value": "{{dob}}",
"renderAs": "Date",
"options": {
"noTime": true
}
},
{
"field": "email_address",
"label": "Email address",
"value": "{{email_address}}",
"renderAs": "Text"
},
{
"field": "gos_level",
"label": "GOS level",
"value": "{{gos_level}}",
"renderAs": "EnumTags",
"options": {
"enumColor": {
"Bronze": "orange",
"Silver": "grey",
"Gold": "gold",
"Platinum": "grey",
"Custodian": "cyan"
}
}
},
{
"label": "Attention flags",
"field": "attention_flags",
"renderAs": "EnumTags",
"value": "{{customer_attention_flags.map(item=>item.attention_flag.flag)}}",
"options": {
"enumColor": {
"Deceased": "red",
"Bequestor": "blue"
}
}
}
]
}
Detail View Screens
Detail views display a single record with all fields editable in a form layout.
Basic Detail View Example
{
"head": {
"title": "Customer Profile",
"key": "customer_profile",
"route": "customer-profile/:id",
"app": "crm"
},
"screenType": "detailView",
"entityKey": "customer",
"detailQuery": "getcustomer",
"sections": [
{
"title": "Basic Information",
"fields": [
"customer_type",
"title",
"name",
"last_name",
"email_address",
"phone"
]
},
{
"title": "Address",
"fields": [
"address_line_1",
"address_line_2",
"suburb",
"state",
"post_code"
]
}
],
"tabs": [
{
"title": "Sales Orders",
"type": "grid",
"field": "sales_orders"
},
{
"title": "Subscriptions",
"type": "grid",
"field": "subscriptions"
}
]
}
Sections
Group related fields into collapsible sections:
{
"sections": [
{
"title": "Contact Information",
"collapsed": false,
"fields": [
"email_address",
"phone",
"mobile"
]
},
{
"title": "Address",
"collapsed": true,
"fields": [
"address_line_1",
"suburb",
"state"
]
}
]
}
Tabs
Create tabbed layouts for related data:
{
"tabs": [
{
"title": "Overview",
"type": "fields",
"fields": ["name", "email", "phone"]
},
{
"title": "Orders",
"type": "grid",
"field": "sales_orders",
"listQuery": "getcustomerorders"
},
{
"title": "Activity",
"type": "timeline",
"field": "history"
}
]
}
Drawer Screens
Drawers are slide-out panels for quick create/edit operations.
Basic Drawer Example
{
"head": {
"title": "Create Customer",
"key": "customer_create_drawer",
"app": "crm"
},
"screenType": "drawer",
"entityKey": "customer",
"width": "large",
"fields": [
"customer_type",
"name",
"last_name",
"email_address",
"phone",
"is_active"
],
"onSuccess": {
"action": "redirect",
"route": "/crm/customer-profile/{{id}}"
}
}
Drawer Sizes
{
"width": "small" // 400px
"width": "medium" // 600px
"width": "large" // 900px
"width": "xlarge" // 1200px
}
Custom Actions
Add custom buttons and actions to screens:
List View Actions
{
"screenType": "listView",
"actions": [
{
"key": "create_customer",
"label": "New Customer",
"icon": "PlusIcon",
"type": "openDrawer",
"drawerKey": "customer_create_drawer"
},
{
"key": "export",
"label": "Export",
"icon": "DownloadIcon",
"type": "export",
"format": "csv"
}
],
"rowActions": [
{
"key": "edit",
"label": "Edit",
"icon": "EditIcon",
"type": "redirect",
"route": "/customer/{{id}}"
},
{
"key": "delete",
"label": "Delete",
"icon": "TrashIcon",
"type": "delete",
"confirm": true,
"confirmMessage": "Are you sure you want to delete this customer?"
}
]
}
Action Types
openDrawer - Open a drawer screen
{
"key": "quick_edit",
"label": "Quick Edit",
"type": "openDrawer",
"drawerKey": "customer_edit_drawer"
}
redirect - Navigate to another screen
{
"key": "view_details",
"label": "View",
"type": "redirect",
"route": "/customer/{{id}}"
}
webhook - Trigger an automation
{
"key": "send_welcome_email",
"label": "Send Welcome Email",
"type": "webhook",
"webhookPath": "/send-welcome-email",
"confirm": true,
"confirmMessage": "Send welcome email to this customer?"
}
export - Export data
{
"key": "export_csv",
"label": "Export CSV",
"type": "export",
"format": "csv"
}
Filtering and Sorting
Default Filters
Apply filters when the screen loads:
{
"screenType": "listView",
"defaultFilters": {
"is_active": true,
"state": "QLD"
}
}
Filter Controls
Add filter UI controls:
{
"filterControls": [
{
"field": "state",
"label": "State",
"type": "select",
"options": ["NSW", "VIC", "QLD", "WA", "SA", "TAS", "NT", "ACT"]
},
{
"field": "is_active",
"label": "Active Only",
"type": "checkbox"
},
{
"field": "created_at",
"label": "Created Date",
"type": "dateRange"
}
]
}
Default Sorting
{
"defaultSorting": [
{
"field": "created_at",
"order": "desc"
}
]
}
Permissions
Control who can view and interact with screens:
{
"permissions": {
"view": ["admin", "manager", "staff"],
"create": ["admin", "manager"],
"edit": ["admin", "manager"],
"delete": ["admin"]
}
}
Conditional Display
Show/hide fields based on conditions:
{
"fields": [
"customer_type",
{
"field": "business_abn",
"condition": {
"field": "customer_type",
"operator": "equals",
"value": "Business"
}
}
]
}
Screen Lifecycle Hooks
Execute logic at various screen lifecycle events:
{
"hooks": {
"onLoad": {
"type": "webhook",
"path": "/track-screen-view"
},
"beforeSave": {
"type": "validate",
"validationFunction": "validateCustomer"
},
"afterSave": {
"type": "notification",
"message": "Customer saved successfully"
}
}
}
Best Practices
1. Use Meaningful Routes
{
"route": "customer-list" // ✅ Good
"route": "list1" // ❌ Bad
}
2. Group Related Fields in Sections
{
"sections": [
{
"title": "Basic Information",
"fields": ["name", "email", "phone"]
},
{
"title": "Address",
"fields": ["address_line_1", "suburb", "state"]
}
]
}
3. Use EnumTags for Status Fields
{
"field": "status",
"renderAs": "EnumTags",
"options": {
"enumColor": {
"active": "green",
"inactive": "red"
}
}
}
4. Add Links to Related Records
{
"field": "customer_name",
"value": "{{customer.name}}",
"renderAs": "Text",
"options": {
"linkProp": "/customer/{{customer_id}}"
}
}
5. Use Drawers for Quick Operations
Use drawers instead of full pages for:
- Quick create/edit
- Status updates
- Adding comments
- Assigning tasks
6. Provide Clear Action Labels
{
"label": "Send Welcome Email" // ✅ Clear
"label": "Action 1" // ❌ Unclear
}
Common Patterns
Master-Detail Screen
{
"screenType": "detailView",
"sections": [
{
"title": "Customer Info",
"fields": ["name", "email"]
}
],
"tabs": [
{
"title": "Orders",
"type": "grid",
"field": "sales_orders",
"actions": [
{
"label": "New Order",
"type": "openDrawer",
"drawerKey": "order_create_drawer"
}
]
}
]
}
Search Screen with Filters
{
"screenType": "listView",
"filterControls": [
{
"field": "search",
"label": "Search",
"type": "text",
"placeholder": "Search by name, email, or CRN"
},
{
"field": "state",
"label": "State",
"type": "select"
}
],
"defaultSorting": [
{ "field": "created_at", "order": "desc" }
]
}
Bulk Action Screen
{
"screenType": "listView",
"bulkActions": [
{
"key": "bulk_delete",
"label": "Delete Selected",
"type": "webhook",
"webhookPath": "/bulk-delete",
"confirm": true
},
{
"key": "bulk_export",
"label": "Export Selected",
"type": "export"
}
]
}
Next Steps
Now that you understand screens, learn about:
- Query Library - Define data sources for your screens
- Automations - Trigger actions from screen buttons
- Entity Hooks - Add validation to forms
- How-To: Create a Master-Detail Screen