Skip to content

How a screen is built - pages, blocks, state, events and actions ​

What it is for ​

Every screen in Spark ERP is a page: a JSON document that arranges blocks and says how they talk to data and to each other. This page explains the model once, so the Block catalog and the Page Designer make sense.

The page ​

A page is the routable screen. It is made of three fixed levels:

text
Page
 └─ Row            (visibility, deferred loading, alignment)
     └─ Column     (visibility, optional card look)
         └─ Item   a block, a form, or a link
  • A block is one UI unit from the catalog: a grid, a date picker, a KPI card.
  • A form is a configured set of fields with validation (see Form Designer).
  • A link is a target route with a label and icon.
  • Overlays are modals and drawers declared on the page and opened by an action.
  • A canvas lets items be placed freely (x, y, size) over the row layout.
  • Deeper nesting happens inside container blocks, not in the page.

An item mounts when the page mounts, even if it is currently hidden, so it keeps reacting to state. Visibility only decides whether it appears. A row or column with nothing visible disappears entirely instead of leaving an empty gap.

Routing, access and menu ​

PartMeaning
route.patternStarts with /. A required parameter must be a :name segment in the pattern. An optional one is a query parameter
accesspublic or authenticated. Authenticated pages check the session before they mount
menuWhere the page appears in navigation: area, group, label, icon, order, and a visibility expression
breadcrumbA label expression and an optional parent route
Login flagsisLoginPage, isSessionExpiredPage, isAccessDeniedPage mark the pages used for sign-in, expired sessions and refusals

Which pages a person can open is also controlled by screen permissions (page:<name>, view). See Permissions.

Responsive layouts ​

The base layout is the smallest screen (xs). Larger breakpoints sm, md, lg, xl can only add or adjust from there. A row, column or item can carry breakpoints overrides. Because nothing can be "hidden at xs and shown at sm", mobile always gets the full base content.

Variants swap parts of a page by rule: by role, by device class (mobile, tablet, desktop) or by a route parameter. The first matching variant wins; none matching falls back to the base page.

The block instance ​

A block on a page is an instance of a block type with values for its properties:

json
{
  "instanceId": "leave-grid",
  "blockType": "core.grid",
  "blockVersion": "1.0.0",
  "properties": {
    "columns": { "source": "static", "value": [{ "name": "employeeName" }, { "name": "status" }] },
    "pageSize": { "source": "static", "value": 25 },
    "refreshTrigger": { "source": "binding", "binding": { "scope": "page", "key": "filterCommittedAt" } }
  },
  "events": {
    "rowClicked": {
      "source": "action-chain",
      "actions": [ { "id": "open", "type": "navigate", "config": { "page": "employee-profile", "params": { "id": "${event.id}" } } } ]
    }
  }
}

A property gets its value from one of three sources, and the catalog says which each property allows:

SourceMeaningWritten as
staticA fixed value{"source":"static","value":"Save"}
bindingA live value from state{"source":"binding","binding":{"scope":"page","key":"total"}}
expressionComputed from state{"source":"expression","expression":"page.qty * page.price"}

An expression-sourced value uses the key expression, not value. Using value is a common mistake and fails validation.

Blocks that take input also have outputs: the value they hold can be written to a scope and key, so other blocks can read it.

State and scopes ​

State is how blocks share values. There are seven scopes, from short-lived to long-lived:

ScopeLivesWho may write
blockOne block instanceThat block
formOne form instanceThat form
pageOne page visit. Cleared on every navigation, including BackThe page
sessionThe person's sign-in sessionThat session
userThe signed-in personThat person
tenantYour whole workspaceNeeds the state:admin right
applicationOne application in your workspaceNeeds the state:admin right

If a value must survive leaving and returning to a page, put it in session or user scope. Page scope is reset by design.

Events and action chains ​

Blocks fire events (a click, a row selected, a value committed). You attach an action chain, a list of steps, to an event. Each step has a type, a config, and optional settings:

Step settingMeaning
conditionA plain expression string, such as event.action == "view". Not an object
retryA number of attempts, or a policy with delay and backoff
onErrorcontinue, stop, retry, compensate, rollback, ignore or custom

The available action types:

ActionWhat it does
navigateGo to a page with parameters
setValueWrite a value to a field in a scope (page.total, or the form scope by default)
callApiCall an endpoint through a named connection (self for this platform). Put dynamic values in params, which are encoded safely
providerCallGet, create, update, patch or delete one record through a data provider
executeWorkflowStart a workflow through the workflow bridge
runIntegrationRun a connector or queue a flow by name
validateCheck an expression; a false result fails the chain with a message
showFieldShow or hide a field
openDialog, closeDialogOpen or close a declared overlay
showToastA short message
showConfirmationAsk the person to confirm; the answer is available to the next step
downloadFile, printDocument, uploadFileDownload, print or upload through the platform's file and print services
arrayJoin, arrayRemoveWhereCombine lists, or remove the row whose field equals a value

Values inside a step's config are filled in with ${...}: ${event.id}, ${page.total}, ${api.result.rows}. This is a plain path lookup, not a full expression. The one transform is ${path|pluck:field}, which turns a list of rows into a list of one field, for example for chart series.

Navigation, dialogs, toasts, downloads and uploads are intents. The step records what should happen and the page host performs it, so the same page JSON works on web and mobile.

Expressions ​

Expressions appear in visibility, expression properties and step conditions. They are evaluated on the screen from state values. They are separate from the simpler conditions used by workflows ({"field","op","value"}) and from the server-side expressions used by rules and computed fields.

Data ​

Grids and KPIs read through the query engine, which pages, filters and sorts for you. A grid gets its data source from the page host. Detail pages use providerCall. See Page data flow and Data providers, views, services.

Block permissions ​

Every block can be visible, enabled and masked according to the person's rights. Rules named widget:<block type> hide or disable a block type for a role. This changes what is shown; protect the data itself with entity, field or record permissions.

Page lifecycle and events ​

  • A page never mounts partially. If anything in its resolved tree fails validation, everything already mounted is removed and all the problems are reported together.
  • A row marked defer mounts only when something activates it, which keeps heavy parts of a page from loading until needed.
  • Page events: PageLoaded, PageClosed, PageNavigated, and NavigationVetoed (a guard, such as unsaved form changes, blocked a move).

Custom blocks ​

When no catalog block fits, build one. See Add a custom block.

Where next ​