Skip to main content

Master-Detail Screens

Learn how to build master-detail screen patterns in Stack9 - a common UI pattern where a list of records (master) is displayed alongside detailed information about a selected record (detail). This guide covers split views, drill-down navigation, and interactive detail panels.

What You'll Learn

  • Creating side-by-side master-detail layouts
  • Implementing drill-down navigation patterns
  • Building interactive detail panels
  • Loading and displaying related data
  • Managing state between master and detail views
  • Optimizing performance for large datasets
  • Mobile-responsive master-detail patterns

Time Required: 45 minutes

Prerequisites

Understanding Master-Detail Patterns

Master-detail is a UI pattern that displays two related views:

Master View (List)

  • Shows a collection of records
  • Allows filtering and searching
  • Provides overview information
  • Enables record selection

Detail View (Details)

  • Shows full information for selected record
  • Displays related data
  • Allows editing and actions
  • Shows contextual information

Common Use Cases

ScenarioMasterDetail
Email ClientEmail listEmail content
Customer ManagementCustomer listCustomer profile
Order ManagementOrder listOrder details with items
Product CatalogProduct listProduct specifications
Document LibraryDocument listDocument preview

Pattern 1: Split View (Side-by-Side)

The split view shows master and detail side-by-side on the same screen.

Example: Customer Master-Detail

Step 1: Create the Master-Detail Screen

File: src/screens/customer_master_detail.json

{
"head": {
"title": "Customers",
"key": "customer_master_detail",
"route": "customer-master-detail",
"app": "crm",
"icon": "UserOutlined"
},
"screenType": "masterDetailView",
"masterConfig": {
"listQuery": "getcustomerlist",
"entityKey": "customer",
"columns": [
{
"field": "name",
"label": "Name",
"value": "{{name}} {{last_name}}",
"renderAs": "Text"
},
{
"field": "email",
"label": "Email",
"value": "{{email}}",
"renderAs": "Text"
},
{
"field": "phone",
"label": "Phone",
"value": "{{phone}}",
"renderAs": "Text"
},
{
"field": "status",
"label": "Status",
"value": "{{status}}",
"renderAs": "EnumTags",
"options": {
"enumColor": {
"Active": "green",
"Inactive": "red"
}
}
}
],
"searchable": true,
"paginate": true,
"pageSize": 20
},
"detailConfig": {
"detailQuery": "getcustomerdetail",
"entityKey": "customer",
"sections": [
{
"title": "Contact Information",
"fields": ["name", "last_name", "email", "phone"]
},
{
"title": "Address",
"fields": ["address_line1", "address_line2", "city", "state", "postal_code"]
},
{
"title": "Additional Details",
"fields": ["status", "customer_type", "date_joined"]
}
],
"relatedLists": [
{
"title": "Orders",
"query": "getordersbycustomer",
"columns": [
{
"field": "order_number",
"label": "Order #",
"value": "{{order_number}}",
"renderAs": "Text"
},
{
"field": "order_date",
"label": "Date",
"value": "{{order_date}}",
"renderAs": "Date"
},
{
"field": "total",
"label": "Total",
"value": "{{total}}",
"renderAs": "Currency"
}
]
}
]
},
"layout": "split",
"splitRatio": "40:60"
}

Step 2: Create Master Query

File: src/query-library/getcustomerlist.json

{
"key": "getcustomerlist",
"name": "getCustomerList",
"connector": "stack9_api",
"queryTemplate": {
"method": "post",
"path": "/customer/search",
"bodyParams": "{\n \"$select\": [\"id\", \"name\", \"last_name\", \"email\", \"phone\", \"status\"],\n \"$where\": {\n \"_is_deleted\": false\n },\n \"$orderBy\": [{\"column\": \"name\", \"order\": \"asc\"}],\n \"$limit\": 20,\n \"$offset\": {{offset}}\n}",
"queryParams": {}
},
"userParams": {
"offset": "0"
}
}

Step 3: Create Detail Query

File: src/query-library/getcustomerdetail.json

{
"key": "getcustomerdetail",
"name": "getCustomerDetail",
"connector": "stack9_api",
"queryTemplate": {
"method": "post",
"path": "/customer/search",
"bodyParams": "{\n \"$select\": [\"*\"],\n \"$where\": {\n \"id\": {{customerId}}\n },\n \"$withRelated\": [\"orders(notDeleted)\"]\n}",
"queryParams": {}
},
"userParams": {
"customerId": ""
}
}

Step 4: Create Orders Query

File: src/query-library/getordersbycustomer.json

{
"key": "getordersbycustomer",
"name": "getOrdersByCustomer",
"connector": "stack9_api",
"queryTemplate": {
"method": "post",
"path": "/order/search",
"bodyParams": "{\n \"$select\": [\"id\", \"order_number\", \"order_date\", \"total\", \"status\"],\n \"$where\": {\n \"customer_id\": {{customerId}},\n \"_is_deleted\": false\n },\n \"$orderBy\": [{\"column\": \"order_date\", \"order\": \"desc\"}]\n}",
"queryParams": {}
},
"userParams": {
"customerId": ""
}
}

What This Creates

  • Left Panel (40%): Scrollable customer list
  • Right Panel (60%): Customer details with related orders
  • Click a customer in the list to see their details
  • Detail panel updates without page reload
  • Related orders shown in expandable section

Pattern 2: Drill-Down Navigation

Drill-down navigates from list to full-page detail view.

Example: Order Management

Step 1: Create Order List Screen

File: src/screens/order_list.json

{
"head": {
"title": "Orders",
"key": "order_list",
"route": "order-list",
"app": "sales",
"icon": "ShoppingCartOutlined"
},
"screenType": "listView",
"listQuery": "getorderlist",
"entityKey": "order",
"columnsConfiguration": [
{
"field": "order_number",
"label": "Order #",
"value": "{{order_number}}",
"renderAs": "Text",
"options": {
"linkProp": "/sales/order-detail/{{id}}"
}
},
{
"field": "customer",
"label": "Customer",
"value": "{{customer.name}} {{customer.last_name}}",
"renderAs": "Text"
},
{
"field": "order_date",
"label": "Date",
"value": "{{order_date}}",
"renderAs": "Date",
"options": {
"format": "DD MMM YYYY"
}
},
{
"field": "total",
"label": "Total",
"value": "{{total}}",
"renderAs": "Currency",
"options": {
"currency": "USD"
}
},
{
"field": "status",
"label": "Status",
"value": "{{status}}",
"renderAs": "EnumTags",
"options": {
"enumColor": {
"Draft": "grey",
"Pending": "orange",
"Processing": "blue",
"Shipped": "cyan",
"Delivered": "green",
"Cancelled": "red"
}
}
}
],
"filters": [
{
"field": "status",
"label": "Status",
"type": "dropdown",
"options": ["Draft", "Pending", "Processing", "Shipped", "Delivered", "Cancelled"]
},
{
"field": "customer_id",
"label": "Customer",
"type": "dropdown",
"entityKey": "customer"
}
],
"actions": [
{
"key": "create",
"label": "New Order",
"type": "drawer",
"drawerKey": "order_create"
}
]
}

Step 2: Create Order Detail Screen

File: src/screens/order_detail.json

{
"head": {
"title": "Order Details",
"key": "order_detail",
"route": "order-detail/:id",
"app": "sales"
},
"screenType": "detailView",
"detailQuery": "getorderdetail",
"entityKey": "order",
"sections": [
{
"title": "Order Information",
"layout": "grid",
"columns": 2,
"fields": [
{
"key": "order_number",
"renderAs": "text",
"readOnly": true
},
{
"key": "order_date",
"renderAs": "date"
},
{
"key": "customer_id",
"renderAs": "reference",
"linkTo": "/crm/customer-detail/{{customer_id}}"
},
{
"key": "status",
"renderAs": "tag"
}
]
},
{
"title": "Order Items",
"type": "relatedList",
"query": "getorderitems",
"entityKey": "order_item",
"columns": [
{
"field": "product",
"label": "Product",
"value": "{{product.name}}",
"renderAs": "Text"
},
{
"field": "quantity",
"label": "Quantity",
"value": "{{quantity}}",
"renderAs": "Number"
},
{
"field": "unit_price",
"label": "Unit Price",
"value": "{{unit_price}}",
"renderAs": "Currency"
},
{
"field": "line_total",
"label": "Total",
"value": "{{line_total}}",
"renderAs": "Currency"
}
],
"editable": true,
"deletable": true
},
{
"title": "Totals",
"layout": "grid",
"columns": 2,
"fields": [
{
"key": "subtotal",
"renderAs": "currency",
"readOnly": true
},
{
"key": "tax_amount",
"renderAs": "currency",
"readOnly": true
},
{
"key": "total",
"renderAs": "currency",
"readOnly": true,
"highlight": true
}
]
},
{
"title": "Payment Information",
"layout": "grid",
"columns": 2,
"fields": [
{
"key": "payment_status",
"renderAs": "tag"
},
{
"key": "payment_method",
"renderAs": "text"
},
{
"key": "payment_transaction_id",
"renderAs": "text"
}
]
},
{
"title": "Notes",
"fields": ["notes"]
}
],
"actions": [
{
"key": "edit",
"label": "Edit Order",
"type": "drawer",
"drawerKey": "order_edit"
},
{
"key": "process_payment",
"label": "Process Payment",
"type": "custom",
"actionType": "process_payment",
"confirmMessage": "Process payment for this order?",
"conditions": [
{
"field": "payment_status",
"operator": "equals",
"value": "Pending"
}
]
},
{
"key": "mark_shipped",
"label": "Mark as Shipped",
"type": "custom",
"actionType": "mark_order_shipped",
"conditions": [
{
"field": "status",
"operator": "equals",
"value": "Processing"
}
]
},
{
"key": "delete",
"label": "Delete",
"type": "delete",
"confirmMessage": "Are you sure you want to delete this order?",
"conditions": [
{
"field": "status",
"operator": "in",
"value": ["Draft", "Pending"]
}
]
}
],
"breadcrumbs": [
{
"label": "Sales",
"link": "/sales"
},
{
"label": "Orders",
"link": "/sales/order-list"
},
{
"label": "{{order_number}}",
"current": true
}
]
}

Step 3: Create Order Detail Query

File: src/query-library/getorderdetail.json

{
"key": "getorderdetail",
"name": "getOrderDetail",
"connector": "stack9_api",
"queryTemplate": {
"method": "post",
"path": "/order/search",
"bodyParams": "{\n \"$select\": [\n \"*\",\n \"customer.name\",\n \"customer.last_name\",\n \"customer.email\"\n ],\n \"$where\": {\n \"id\": {{orderId}}\n },\n \"$withRelated\": [\n \"customer(notDeleted)\",\n \"order_items(notDeleted)\",\n \"order_items(notDeleted).product(notDeleted)\"\n ]\n}",
"queryParams": {}
},
"userParams": {
"orderId": ""
}
}

Pattern 3: Tabbed Detail View

Display related information in tabs within the detail view.

Example: Customer Profile with Tabs

File: src/screens/customer_profile.json

{
"head": {
"title": "Customer Profile",
"key": "customer_profile",
"route": "customer-profile/:id",
"app": "crm"
},
"screenType": "detailView",
"detailQuery": "getcustomerprofile",
"entityKey": "customer",
"layout": "tabbed",
"tabs": [
{
"key": "overview",
"label": "Overview",
"icon": "UserOutlined",
"sections": [
{
"title": "Contact Information",
"layout": "grid",
"columns": 2,
"fields": ["name", "last_name", "email", "phone"]
},
{
"title": "Address",
"layout": "grid",
"columns": 2,
"fields": ["address_line1", "address_line2", "city", "state", "postal_code", "country"]
}
]
},
{
"key": "orders",
"label": "Orders",
"icon": "ShoppingCartOutlined",
"type": "relatedList",
"query": "getordersbycustomer",
"entityKey": "order",
"columns": [
{
"field": "order_number",
"label": "Order #",
"value": "{{order_number}}",
"renderAs": "Text",
"options": {
"linkProp": "/sales/order-detail/{{id}}"
}
},
{
"field": "order_date",
"label": "Date",
"value": "{{order_date}}",
"renderAs": "Date"
},
{
"field": "total",
"label": "Total",
"value": "{{total}}",
"renderAs": "Currency"
},
{
"field": "status",
"label": "Status",
"value": "{{status}}",
"renderAs": "EnumTags"
}
],
"actions": [
{
"key": "create_order",
"label": "New Order",
"type": "drawer",
"drawerKey": "order_create",
"defaultValues": {
"customer_id": "{{id}}"
}
}
]
},
{
"key": "subscriptions",
"label": "Subscriptions",
"icon": "SyncOutlined",
"type": "relatedList",
"query": "getsubscriptionsbycustomer",
"entityKey": "subscription"
},
{
"key": "communications",
"label": "Communications",
"icon": "MailOutlined",
"sections": [
{
"title": "Email History",
"type": "relatedList",
"query": "getemailsbycustomer"
},
{
"title": "Notes",
"type": "relatedList",
"query": "getnotesbycustomer",
"editable": true,
"createInline": true
}
]
},
{
"key": "activity",
"label": "Activity",
"icon": "HistoryOutlined",
"type": "timeline",
"query": "getcustomeractivity",
"timelineConfig": {
"dateField": "created_at",
"titleField": "activity_type",
"descriptionField": "description",
"iconField": "icon"
}
}
],
"actions": [
{
"key": "edit",
"label": "Edit",
"type": "drawer",
"drawerKey": "customer_edit"
},
{
"key": "send_email",
"label": "Send Email",
"type": "drawer",
"drawerKey": "send_customer_email"
}
]
}

Pattern 4: Modal Detail View

Show detail in a modal/drawer overlay.

Example: Quick View Order Details

Screen Configuration:

{
"head": {
"title": "Orders",
"key": "order_list_quick_view",
"route": "order-list-quick",
"app": "sales"
},
"screenType": "listView",
"listQuery": "getorderlist",
"entityKey": "order",
"columnsConfiguration": [],
"rowActions": [
{
"key": "quick_view",
"label": "Quick View",
"icon": "EyeOutlined",
"type": "drawer",
"drawerConfig": {
"width": "60%",
"detailQuery": "getorderdetail",
"sections": [
{
"title": "Order Summary",
"fields": ["order_number", "customer_id", "order_date", "status"]
},
{
"title": "Items",
"type": "relatedList",
"query": "getorderitems"
}
],
"actions": [
{
"key": "full_details",
"label": "View Full Details",
"type": "link",
"link": "/sales/order-detail/{{id}}"
}
]
}
},
{
"key": "edit",
"label": "Edit",
"icon": "EditOutlined",
"type": "drawer",
"drawerKey": "order_edit"
}
]
}

Advanced Patterns

Pattern 5: Hierarchical Master-Detail

Display hierarchical data with expandable rows.

Example: Product Categories and Products

{
"head": {
"title": "Products",
"key": "product_hierarchy",
"route": "product-hierarchy",
"app": "products"
},
"screenType": "hierarchyView",
"masterQuery": "getcategorylist",
"detailQuery": "getproductsbycategory",
"hierarchyConfig": {
"expandable": true,
"defaultExpanded": false,
"masterColumns": [
{
"field": "name",
"label": "Category",
"value": "{{name}}",
"renderAs": "Text"
},
{
"field": "product_count",
"label": "Products",
"value": "{{product_count}}",
"renderAs": "Number"
}
],
"detailColumns": [
{
"field": "sku",
"label": "SKU",
"value": "{{sku}}",
"renderAs": "Text"
},
{
"field": "name",
"label": "Product",
"value": "{{name}}",
"renderAs": "Text"
},
{
"field": "price",
"label": "Price",
"value": "{{price}}",
"renderAs": "Currency"
},
{
"field": "stock_quantity",
"label": "Stock",
"value": "{{stock_quantity}}",
"renderAs": "Number"
}
]
}
}

Pattern 6: Comparison View

Compare multiple records side-by-side.

{
"head": {
"title": "Product Comparison",
"key": "product_comparison",
"route": "product-comparison",
"app": "products"
},
"screenType": "comparisonView",
"listQuery": "getproductlist",
"maxCompare": 3,
"compareFields": [
{
"key": "name",
"label": "Product Name",
"type": "text"
},
{
"key": "price",
"label": "Price",
"type": "currency",
"highlight": "lowest"
},
{
"key": "stock_quantity",
"label": "Stock",
"type": "number"
},
{
"key": "rating",
"label": "Rating",
"type": "rating",
"highlight": "highest"
},
{
"key": "description",
"label": "Description",
"type": "richtext"
}
]
}

Mobile-Responsive Patterns

Responsive Layout Configuration

{
"layout": "split",
"splitRatio": "40:60",
"responsive": {
"mobile": {
"layout": "stacked",
"showMasterFirst": true
},
"tablet": {
"layout": "split",
"splitRatio": "50:50"
},
"desktop": {
"layout": "split",
"splitRatio": "40:60"
}
}
}

Performance Optimization

1. Lazy Loading Detail Data

{
"detailConfig": {
"loadOnSelect": true,
"cacheDetails": true,
"cacheDuration": 300000
}
}
{
"title": "Orders",
"type": "relatedList",
"query": "getordersbycustomer",
"pagination": {
"enabled": true,
"pageSize": 10,
"showSizeChanger": true
}
}

3. Virtual Scrolling for Large Lists

{
"masterConfig": {
"virtualScroll": true,
"itemHeight": 48,
"overscan": 5
}
}

Best Practices

1. Design for Context

Good: Contextual Actions

{
"actions": [
{
"key": "email_customer",
"label": "Email {{customer.name}}",
"icon": "MailOutlined"
}
]
}

2. Provide Clear Navigation

Good: Breadcrumbs

{
"breadcrumbs": [
{"label": "Home", "link": "/"},
{"label": "Customers", "link": "/crm/customers"},
{"label": "{{name}}", "current": true}
]
}

3. Show Loading States

{
"detailConfig": {
"loadingStrategy": "skeleton",
"loadingText": "Loading customer details..."
}
}

4. Handle Empty States

{
"relatedLists": [
{
"title": "Orders",
"query": "getordersbycustomer",
"emptyState": {
"icon": "ShoppingCartOutlined",
"title": "No orders yet",
"description": "This customer hasn't placed any orders.",
"action": {
"label": "Create First Order",
"type": "drawer",
"drawerKey": "order_create"
}
}
}
]
}

5. Keyboard Navigation

Enable keyboard shortcuts for master-detail navigation:

  • ↑/↓: Navigate master list
  • Enter: Open detail
  • Esc: Close detail/return to list
  • Tab: Navigate between sections

Troubleshooting

Detail Not Loading

Problem: Detail panel shows loading indefinitely

Solutions:

  1. Check detailQuery is defined correctly
  2. Verify query parameter matches (e.g., {{id}})
  3. Check query returns data for the ID
  4. Look for errors in browser console

Problem: Related list shows no data

Solutions:

  1. Verify foreign key relationship is correct
  2. Check query filters related records properly
  3. Ensure $withRelated is configured
  4. Test query independently

Performance Issues

Problem: Screen is slow with many records

Solutions:

  1. Enable virtual scrolling for master list
  2. Implement pagination for related lists
  3. Use lazy loading for detail data
  4. Optimize queries with proper indexes
  5. Cache detail data when appropriate

Mobile Layout Broken

Problem: Layout doesn't work on mobile

Solutions:

  1. Configure responsive breakpoints
  2. Use stacked layout for mobile
  3. Test on actual devices
  4. Adjust splitRatio for tablets

Summary

You now understand:

  • Split view master-detail layouts
  • Drill-down navigation patterns
  • Tabbed detail views
  • Modal detail overlays
  • Hierarchical data display
  • Mobile-responsive patterns
  • Performance optimization techniques
  • Best practices for master-detail UX

Next Steps