Exploring ServiceNow UI Builder: Data, Events, Security, and Workspace Design
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
7m ago
Scaling the ServiceNow UI Builder is dramatically simpler if you think about it beyond a mere page container. The production-ready experience is a system that consists of the following: the app shell defines the workspace container, the pages determine routes and parameters, data resources define the data contract, bindings link data to the components, and the event handlers perform actions in response to events from the user or data sources.
In this article, we will create such a system based on the Incident Operations workspace. The examples are provided using the most recent ServiceNow terminology when possible, but pointing out the places where the naming conventions or capabilities differ depending on the family, UI Builder version installed, scope, and experience type.
For the Incident Operations example, use this flow:
Workspace App Shell
↓
Experience → Page Collection → Page Variant → Page Parameters
↓ ↓
Components ← Bindings ← Data Resources / Data Brokers
↓ ↓
Events → Event Mappings → Handlers → Navigation, State, Scripts, Actions
1. Events and event mapping: an event is not an action
A UI Builder event describes something that happened. A button was clicked, a row was selected, a data resource completed, or a page became ready. The event itself does not navigate, refresh, or show a message until it is mapped to an event handler.
A useful mental model is:
Event source → Event payload → Event mapping → Handler → Result
For example, create an Incident list page with a local record data resource named incident_list. Bind the list to the resource result. On the list's row-clicked event, add an event mapping and select Link to destination. The destination page should declare the parameters it needs, usually table and sys_id. Map the exact values exposed by the row event payload to those parameters.
Do not assume that every component exposes a payload at @payload.record.sys_id. Payload shape is component- and release-specific. Open the event payload picker, inspect the available fields, or temporarily log event.payload in a Client Script. The correct binding might be a direct @payload.sys_id, a nested record path, or a component-specific identifier.
Page events add another level. A handled event is an exposed page event that other mappings can consume. A dispatched event is commonly used as a relay from a component or page toward another handler. A data resource may expose lifecycle events such as Data Fetch Succeeded and Data Fetch Failed, while newer procedures may show Operation Succeeded and Operation Failed. Use the names presented by the target instance instead of copying labels from an older screenshot.
You should not assume that all the components expose their payload via @payload.record.sys_id. It depends on the component and its release. Open the event payload picker to see the fields and/or temporarily log event.payload in a Client Script. The correct binding could be either @payload.sys_id directly, a nested record path or a particular component-specific one.
A new level of events becomes available when you work with Page events. A handled event is an exposed Page event that could be used by other mappings. A dispatched event is often used as a relay from the component or the page to another event handler. Data resources could expose some lifecycle events like Data Fetch Succeeded and Data Fetch Failed, while more modern procedures could expose Operation Succeeded and Operation Failed. Always use names provided by the target instance, rather than copy-paste from the old screen capture.
2. Client State Parameters: page-specific run-time state
Client state parameters are variables with data type that are available in the current UI Builder page. These are helpful for filter settings, selected records, display texts, counters, and other transient state information that is shared between components. It does not include database columns, persistent session data, or any other mechanism of passing state information between pages.
You can create a state parameter on the client state tab of the current page. Current versions of documentation provide options for various data types like String, Number, Boolean, and JSON. The name of the parameter should be defined without spaces along with the initial value. Components can be bound using Dynamic data binding component or expression like @STate.incidentQuery.
Example of an Incident page could be:
incidentQuery: String = "active=true"
incidentTitle: String = "Open incidents"
selectedIncident: String = ""
Bind incidentQuery to an appropriate filter input for the Incident data resource and bind incidentTitle to the list header. The button could use the Update client state parameter handler to modify the query. With the data resource input being bound to that state parameter and the resource evaluating that input, the list could re-query and draw.
The crucial point here is when the resource evaluates that input. A state change does not necessarily trigger a refresh for every data resource. Determine if the data resource is configured for eager evaluation, explicit evaluation, or lifecycle handlers. In case of explicit evaluation of the data resource, first update the state and then explicitly refresh/execute the flow.
3. Client Scripts: orchestration, not legacy form scripting
UI Builder Client Script is a page-specific handler running on the client side in the browser environment that was authored via the Now Code Editor and associated with the event. It is not the same as a legacy Core UI form Client Script. Do not assume anything about g_form, onLoad, onChange, and onSubmit.
The common handler will get the object with such properties as api, event, helpers, and imports. The API itself will vary depending on the context of the script and the release. The following snippet demonstrates how the orchestration script could be implemented:
JavaScript
function handler({ api, event }) {
const incidentId = event.payload?.sys_id;
if (incidentId) {
api.setState('selectedIncident', incidentId);
}
// Use the exact configured data-resource ID from the page.
// Refresh is normally used for a non-mutating resource.
api.data.incident_list.refresh();
}
The script may access page context, client state, data resources, and event payload information. It may modify a declared state parameter. However, it can fire an event only if the event has been declared in the dispatched-events list of the page definition. Choose an easy-to-understand and descriptive event name like INCIDENT_SELECTED or FILTER_CHANGED.
Do not try to mutate api.state and/or any nested objects directly. Modifications to the state are asynchronous, so if you need the previous value to calculate the new one, use the callback form of the operation (as documented for the API).
Call execute() only when the resource is able to do so, based on its type and configuration. Information about capability and configuration of the API is provided in the API reference, including declaration of inputs and the mutates_server_data contract. Do not execute an execution method simply because a resource is present in api.data.
4. Declarative Actions: a business interaction layer
Declarative Actions let teams add behavior to form, list, related-list, field-decorator, and similar Next Experience surfaces without taking ownership of the entire page. They are different from traditional UI Actions, even though the names sound similar.
A realistic example is a Check SLA action on an Incident list:
1.Create a List Action assignment for the Incident table.
2.Make it active and define whether a record selection is required.
3.Implement it as a UXF Client Action when the action needs to send a browser-side payload.
4.Expose a payload value such as recordSysId.
5. Map the declarative-action event to a handled UI Builder event such as OPEN_SLA_VIEW.
6. Bind recordSysId to a data resource that retrieves SLA information.
7. Navigate to an SLA page, open a modal, or show a result message.
Current documentation also describes a UI interaction implementation for some form, list, and related-list action surfaces. Treat that as a release- and surface-specific option, not as a universal replacement for the UXF Client Action pattern. Visibility can also be affected by the table, view, application, scope, active flag, experience restriction, and user permissions.
5. UX App Routes and Navigation
Navigation works best when the destination page has a deliberate contract. Create a record page with required parameters such as table and sys_id. Then create a UX App Route that maps a reusable route path to the relevant screen collection or page collection.
On the source list, configure Link to destination. Map the row payload to the destination's required fields. On the record page, consume those values as page context:
@context.props.table
@context.props.sys_id
The record data resource can use those context values as its table and record inputs. This gives a clean chain:
row payload → destination parameters → @context.props.* → data resource → form/detail component
Optional parameters are different from required route segments. Current documentation describes optional parameters as name/value pairs whose order does not matter. They are useful for filters or view hints, but the page should still behave safely when they are absent.
Side navigation is configured at the workspace app-shell or experience level. It is not the same mechanism as a record route. In the current procedure, the workspace navigation editor limits the number of selected entries, so keep the primary navigation short and use in-page actions for secondary destinations.
6. Custom Components: low-code first, then code when needed
There are two officially recognized approaches to building custom user interfaces:
•Component Builder: low-code approach to create Macroponent Components using pre-existing components, properties, states, data sources, policies, and events.
•NOW CLI & Next Experience UI Framework: code approach to build custom web components using JavaScript, HTML, CSS, npm, and the ServiceNow CLI.
Choose Component Builder when the use case can be solved using pre-existing components and bindings. Choose NOW CLI when you require unique visualization, custom interaction model, or rendering approach which is not available via low-code toolbox.
A potentially reusable SLA Case Summary component could potentially offer a table property that is bound to a local data resource, present a short list of SLA cases, and an event that triggers viewing the case. The containing page would map this event to a route to the workspace. Make sure the public properties of the component remain limited, and describe what kind of data structure you expect.
A component is not a data source on its own. Use or derive the correct data source component, configure all the necessary inputs, preview JSON, and bind the component to the correct path. Components and any related UX records will require a migration plan when the application is installed or update set is applied. Don’t forget about the application scope and security policy.
7. REST API Integration
In current UI Builder documentation, REST is a data-resource type. In older ServiceNow documentation, the record behind is called REST Data Broker. The integration approach is the same: expose the server-side endpoint, add REST resource to the page, set up inputs, bind returned JSON to components.
For instance, for Vendor Incident Summary resource that takes vendor_id input parameter and returns:
JSON
{
"items": [
{
"number": "INC0012345",
"short_description": "VPN failure",
"state": "In progress"
}
],
"total": 1
}
Set up the resource input from @STate.vendor_id or @context.props.vendor_id. Bind a list to actual response JSON path like:
@Data.vendor_incident_summary.result.items
Do not make guesses about the output JSON shape; open resource Preview JSON and bind to actual JSON structure. Store credentials, authentication, input validation and authorization into Server-side REST Message, Scripted REST API, Data Broker or other secured layer. Do not store credentials or secrets into page Client Script.
Test the resource with a success response, with 401/403 response, with timeout and with malformed JSON response. Map the failure event of the resource to visible error state for the user without revealing server-specific errors.
8. GraphQL Data Broker
GraphQL works well if the page requires a small and nested response, rather than many broad requests. The definition of the broker consists of the GraphQL request and properties exposed as UI Builder parameters.
A record page could expose the required studentSysId property and bind it to @context.props.sys_id. The request will use a variable, instead of a hardcoded string:
query GetStudent($studentSysId: ID!) {
getStudent(id: $studentSysId) {
name
incidents {
number
shortDescription
state
}
}
}
Note that the actual output path will include the resource ID and application namespace, so check the Output or Preview pane before the binding. In case of a nested repeater, usually, the binding will be made via @item.number, or the corresponding field exposed in the response.
Select only those fields that are used on the page. Over-fetching will make bindings more complex and increases the load on both server and browser. ServiceNow release notes also mention excessively made GraphQL requests ,wide caching queries, and excessive number of data brokers or controllers.
6.Page Properties and Parameters
The term “page property” is overloaded in ServiceNow contexts. A page parameter is a value provided via page routing and made available on the page as context. The UX Page Property record is another metadata construct commonly used in workspace and app-shell settings.
For a record page, declare the following:
Required: table, sys_id
Optional: query
While developing, use test values such as incident and a known Incident sys_id. In the absence of test values, a data source bound to context might not display any data while editing, even though the runtime route will pass some values.
Bind the record data source to the following:
@context.props.table
@context.props.sys_id
Page context is immutable. When a user modifies a filter or selection, remember that value in the client-side state rather than attempting to change the route property. If persistence beyond navigation is required, consider making the value an explicit destination parameter or using server-side persistence mechanisms.
10. Now Experience Framework
Terminology for the platform has evolved since the beginning. Modern product descriptions typically use the Next Experience UI Framework terminology, but menus and other materials may still refer to the Now Experience Framework. The framework is the web-component based architecture used in the construction of current ServiceNow interface designs. UI Builder is the designer interface that uses components to build experiences and workspace pages.
An experience is the container element. It consists of an application scope, base URL path, app shell, pages, page variants, and experience settings. The app shell includes persistent elements like navigation, headers, user menus, search, and workspace chrome.
Three different layers should be kept separate:
•Framework: the component architecture layer.
•UI Builder: the visual designer interface layer.
•Workspace configuration: the app shell, routing, navigation, pages, variants, audiences, and data connection elements that comprise the functional experience.
This makes sense when converting a low-code page into a custom component or determining if Workspace Builder in App Engine Studio will suffice for the job.
11. Workspace Configuration
Configure the whole experience using UI Builder, that is, Workspace App Shell, landing path, pages, routes, page variants, audiences, data resources, custom components, contextual panels, and advanced event behavior. Workspace Builder in App Engine Studio can be used for simple workspace configuration in case the requirements and supported configuration surface will suffice.
Example structure of a Customer Support Workspace might be as follows:
Workspace App Shell
├── Home: KPIs and queue summary
├── Cases: list with filters and client state
├── Case record: required table and sys_id parameters
└── Manager variant: audience and condition for elevated view
In the Cases page, use the local record data resource, bind the result to a list, and apply row event for navigation to the Case record page. In the record page, use the route parameters to bind to the record resource. Client state for the selected queue or temporary filter should be used. Declarative actions are used for business processes like Check SLA or Assign to me.
If the workspace has been configured in App Engine Studio and all that is needed is simple home, list, and record configuration, then Workspace Builder would suffice. UI Builder should be
12. Debugging and Troubleshooting
Employ the bottom-up approach in a consistent manner:
1.Inspect the data resource. Check the required inputs, bindings, and Preview JSON.
2.Inspect the binding path. Make sure the component consumes an array, object, or scalar at the real returned path.
3.Inspect the event. Make sure the name of the event, payload, mapping, and handler inputs are checked.
4.Inspect the runtime behavior. Use the Next Experience Developer tools for inspection of components, properties, state, actions, events, logs, and traces.
5.Inspect the authorization. The resource error can be the issue of ACL or data_broker authorization, but not of the binding one.
6.Clear the proper cache. Use the specified UI Builder cache command and the browser cache process for the release when the stale metadata is suspected.
The usual failure pattern is an empty list. The component could be alright, while the resource input is empty, the route parameter lacks the test value, the wrong output path is used, or ACL removed the records. Preview JSON and the resolved input are faster proofs than the property changes of the component.
If you found this article useful, please mark it as Helpful. It helps others find the content more easily 👍
