Skip to main content
Version: Latest (4.6.0)

Platform Views & UI Layout Control

Configure UI controls for ServiceNow platform lists and forms. Covers UI Actions (sys_ui_action), UI Policies (sys_ui_policy), and UI Formatters (sys_ui_formatter). Use this guide when configuring buttons on forms or lists, field visibility/mandatory rules, formatting a form, adding sections, activity streams, process flows, dynamic field behavior, or role-restricted actions.

Choosing the Right Approach

NeedUseAPI
Button/link on form or listUI ActionsUiAction
Field visibility/mandatory/read-only based on conditionUI PoliciesUiPolicy
Non-field content on forms (activity, process flow)UI FormattersRecord API
Configure list columns and orderListsList
Different form layout per role/group/personaViewsRecord API
Auto-switch layout when condition metView RulesRecord API
Hide New/Edit buttons, role-based list actionsList ControlsRecord API

Views vs View Rules vs UI Policies

ScenarioUse
Whole form layout changes per role/group (different fields/sections)View
Whole form layout switches automatically based on condition/device/stateView Rule
Specific fields hide/show/mandatory/read-only when condition metUI Policy
Control list buttons (New/Edit) or disable paginationList Control

Views vs ACLs

IntentUse
Certain fields/sections should not appear in the form for some usersViews -- fields absent from the form entirely
Restrict who can read/write/delete records or fields (data security)ACLs -- security enforcement

Avoidance

  • Never create sys_ui_formatter records for Activity or Attached Knowledge -- they already exist globally.
  • Never create custom formatters -- not supported in Fluent.
  • Never use disabled in UI Policy actions -- use readOnly instead (the plugin maps it).
  • Never place a formatter in a section with no other elements -- it will not render.
  • Never have a UI Action script return a value -- the script field must never return anything.
  • Never skip the view uniqueness check -- sys_ui_view is global and title must be unique across all scopes.
  • Never create lists for tables that don't exist yet -- define the table first.
  • Never use personal list preferences (sys_ui_list_user) in application code -- those are user-specific.
  • Never mix basic and advanced relationship fields in view configurations -- use one pattern or the other.
  • Never use Business Rules, Client Scripts, or UI Policies for view switching -- always use View Rules (sysrule_view).
  • Never combine omit_*_button: true with *_roles for the same capability -- the omit flag overrides role permissions.

UI Actions

Use the UiAction API from @servicenow/sdk/core. Every UI Action must have $id, table, name, and actionName.

Key Guidance

  1. Be explicit about placement: Set form.showButton, list.showButton, etc. to control where the action appears. If blank, the action may not appear anywhere useful.
  2. Set visibility mode: Use showInsert: true for new record forms, showUpdate: true for existing record forms and list views.
  3. Client vs. server scripts: Set client.isClient: true for client-side execution. When isClient is true, use client.onClick for the trigger and script for the function definition.
  4. Set a style on form/list objects -- 'primary', 'destructive', or 'unstyled'.
  5. Use condition to control when the action is visible (e.g., current.canWrite()).
  6. The script field must never return anything.

UI Action Properties

PropertyTypeRequiredDescription
$idNow.ID[string]YesUnique identifier
tablestringYesTable this UI Action is associated with
namestringYesDisplay name
actionNamestringYesUnique identifier usable in scripts
activebooleanNoWhether the action is available
showInsertbooleanNoShow on form in insert mode (before save)
showUpdatebooleanNoShow on form in update mode (after save)
showQuerybooleanNoShow on list when a filter is applied
showMultipleUpdatebooleanNoAllow triggering on multiple selected records
conditionstringNoScript/condition controlling visibility
scriptstringNoScript to execute when triggered
hintstringNoTooltip text
ordernumberNoButton/link position
isolateScriptbooleanNoRun script in isolated scope
roles(string or Role)[]NoRoles that can see/execute the action
includeInViewsstring[]NoViews where the action appears
excludeFromViewsstring[]NoViews where the action is excluded
messagesstring[]NoMessages for client scripts

Form Properties

PropertyTypeDescription
form.showButtonbooleanAdd button to form
form.showLinkbooleanAdd link to form
form.showContextMenubooleanAdd to right-click menu
form.stylestring'primary', 'destructive', or 'unstyled'

List Properties

PropertyTypeDescription
list.showButtonbooleanAdd button to list
list.showLinkbooleanAdd link to list
list.showContextMenubooleanAdd to right-click menu
list.stylestring'primary', 'destructive', or 'unstyled'
list.showListChoicebooleanAdd to choice field dropdowns
list.showBannerButtonbooleanAdd button in list banner
list.showSaveWithFormButtonbooleanSave form before executing

Client Properties

PropertyTypeDescription
client.isClientbooleanScript runs on client (true) or server (false)
client.isUi11compatiblebooleanCompatible with UI11
client.isUi16CompatiblebooleanCompatible with UI16
client.onClickstringJavaScript to run when clicked

Workspace Properties

PropertyTypeDescription
workspace.isConfigurableWorkspacebooleanEnable for Configurable Workspace
workspace.showFormButtonV2booleanV2 button rendering
workspace.showFormMenuButtonV2booleanV2 menu rendering
workspace.clientScriptV2stringV2 client script model code

UI Action Examples

Form button that refreshes the page

import '@servicenow/sdk/global';
import { UiAction } from '@servicenow/sdk/core';

export const refreshAction = UiAction({
$id: Now.ID['refresh-page'],
table: 'test_table',
name: 'Refresh Page',
actionName: 'refresh_page',
active: true,
hint: 'Refresh the current page',
showUpdate: true,
showInsert: true,
form: {
showButton: true,
style: 'primary',
},
script: `window.location.reload();`,
});

List button with info message

import '@servicenow/sdk/global';
import { UiAction } from '@servicenow/sdk/core';

export const listAction = UiAction({
$id: Now.ID['list-info'],
table: 'test_table',
name: 'Show Info',
actionName: 'show_info',
active: true,
showUpdate: true,
list: {
showBannerButton: true,
showButton: true,
style: 'primary',
},
script: `gs.addInfoMessage('button pressed');`,
});

Conditional action with roles and multi-row update

import '@servicenow/sdk/global';
import { UiAction } from '@servicenow/sdk/core';

export const adminAction = UiAction({
$id: Now.ID['admin-action'],
table: 'test_table',
name: 'Admin Action',
actionName: 'admin_action',
active: true,
showUpdate: true,
showInsert: true,
showMultipleUpdate: true,
list: { showButton: true, style: 'primary' },
form: { showButton: true, style: 'primary' },
condition: `current.canWrite();`,
roles: ['admin'],
});

Client-side workspace-compatible action

import '@servicenow/sdk/global';
import { UiAction } from '@servicenow/sdk/core';

export const workspaceAction = UiAction({
$id: Now.ID['workspace-action'],
table: 'test_table',
name: 'Workspace Action',
actionName: 'workspace_action',
active: true,
showUpdate: true,
showInsert: true,
form: { showButton: true, style: 'primary' },
client: { isClient: true, isUi16Compatible: true },
roles: ['itil'],
workspace: {
showFormButtonV2: false,
showFormMenuButtonV2: false,
isConfigurableWorkspace: false,
},
script: Now.include('../../client/test.js'),
});

UI Policies

Use the UiPolicy API from @servicenow/sdk/core. Every UI Policy must have $id, table, and shortDescription.

Key Guidance

  1. Use encoded query syntax for conditions -- e.g., "priority=1^state!=6".
  2. Action properties use boolean | 'ignore' -- use true/false to set, 'ignore' to leave unchanged.
  3. Use readOnly (not disabled) -- it maps directly to ServiceNow's disabled field.
  4. Prefer declarative actions over scripts -- better performance and less error-prone.
  5. reverseIfFalse defaults to true -- when conditions are false, actions are automatically inverted.
  6. Choice fields use stored values -- use the Value column, not the Label column. Right-click field > Show Choice List to find stored values.

UI Policy Properties

PropertyTypeRequiredDefaultDescription
$idNow.ID[string]YesUnique identifier
tablestringYesTable the policy applies to
shortDescriptionstringYesBrief description
activebooleanNotrueWhether policy is active
globalbooleanNotrueApply to all views
onLoadbooleanNofalseRun when form loads
reverseIfFalsebooleanNotrueInvert actions when condition is false
inheritbooleanNofalseApply to extending tables
ordernumberNo100Execution order (lower first)
conditionsstringNoEncoded query for when policy applies
runScriptsbooleanNofalseEnable script execution
scriptTruestringNoJS when condition is true (wrap in function onCondition() {})
scriptFalsestringNoJS when condition is false
uiTypestringNo'desktop''desktop', 'mobile-service-portal', or 'all'
isolateScriptbooleanNofalseRun scripts in isolated scope
actionsarrayNoField actions (see below)
relatedListActionsarrayNoRelated list visibility controls (see below)

Field Action Properties

PropertyTypeRequiredDefaultDescription
fieldstringYesTarget field name
visibleboolean | 'ignore'No'ignore'Show/hide field
readOnlyboolean | 'ignore'No'ignore'Read-only/editable (maps to ServiceNow disabled)
mandatoryboolean | 'ignore'No'ignore'Required/optional
clearedbooleanNofalseClear field value when condition met
valuestringNoSet field to specific value
fieldMessagestringNoMessage to display near field
fieldMessageTypestringNo'none''info', 'warning', 'error', or 'none'
PropertyTypeRequiredDescription
$idNow.ID[string]YesUnique identifier
liststringYesRelated list ID: plain GUID or table.field format
visibleboolean | 'ignore'NoShow/hide the related list

The plugin automatically adds/removes the REL: prefix for GUIDs when transforming to/from ServiceNow.

Condition Syntax

AND operator (^) -- all conditions must be true:

conditions: "priority=1^state=2"

OR operator (^OR) -- at least one must be true:

conditions: "priority=1^ORpriority=2"

Common operators: =, !=, >, >=, <, <=, IN, LIKE, NOT LIKE, STARTSWITH, ANYTHING, EMPTYSTRING, SAMEAS, NSAMEAS, BETWEEN

Choice field patterns:

PatternCondition Example
Specific valuecategory=hardware
Multiple values (OR)categoryINhardware,software
Not a valuecategory!=hardware
Any value selectedcategory!=NULL
No value selectedcategory=NULL

UI Policy Examples

Progressive disclosure

import { UiPolicy } from '@servicenow/sdk/core';

export const categoryPolicy = UiPolicy({
$id: Now.ID['incident_category_policy'],
table: 'incident',
shortDescription: 'Show subcategory when category is selected',
onLoad: true,
conditions: 'category!=NULL',
actions: [
{ field: 'subcategory', visible: true, mandatory: true },
],
});

State-based read-only

import { UiPolicy } from '@servicenow/sdk/core';

export const closedPolicy = UiPolicy({
$id: Now.ID['closed_incident_policy'],
table: 'incident',
shortDescription: 'Make fields read-only for closed incidents',
onLoad: true,
conditions: 'state=7',
actions: [
{ field: 'short_description', readOnly: true },
{ field: 'description', readOnly: true },
{ field: 'priority', readOnly: true },
{ field: 'assignment_group', readOnly: true },
],
});

Field clearing on condition change

import { UiPolicy } from '@servicenow/sdk/core';

export const reopenPolicy = UiPolicy({
$id: Now.ID['incident_reopen_policy'],
table: 'incident',
shortDescription: 'Clear resolution fields when incident is reopened',
onLoad: true,
conditions: 'state!=6^state!=7',
actions: [
{ field: 'resolution_code', cleared: true },
{ field: 'close_notes', cleared: true },
{ field: 'resolved_at', cleared: true },
{ field: 'resolved_by', cleared: true },
],
});

Default values with field messages

import { UiPolicy } from '@servicenow/sdk/core';

export const securityPolicy = UiPolicy({
$id: Now.ID['security_incident_policy'],
table: 'incident',
shortDescription: 'Set defaults for security incidents',
onLoad: true,
conditions: 'categoryLIKEsecurity',
actions: [
{
field: 'priority',
value: '2',
readOnly: true,
fieldMessage: 'Priority automatically set to High for security incidents',
fieldMessageType: 'info',
},
{
field: 'assignment_group',
mandatory: true,
fieldMessage: 'Security incidents must be assigned immediately',
fieldMessageType: 'warning',
},
],
});
import { UiPolicy } from '@servicenow/sdk/core';

export const highPriorityPolicy = UiPolicy({
$id: Now.ID['high_priority_policy'],
table: 'incident',
shortDescription: 'Show additional details for high priority incidents',
onLoad: true,
conditions: 'priority=1^ORpriority=2',
actions: [
{ field: 'work_notes', visible: true, mandatory: true },
{ field: 'impact', mandatory: true },
],
relatedListActions: [
{ $id: Now.ID['show_tasks'], list: 'incident_task.parent', visible: true },
{ $id: Now.ID['show_cis'], list: 'task_ci.task', visible: true },
],
});

UI Policy Scripts

Set runScripts: true to enable scripts. Scripts execute client-side and have access to g_form, g_user, g_scratchpad, and other client APIs.

All scripts must be wrapped in function onCondition() { ... } format.

Best practices:

  • Prefer field actions over scripts for simple behaviors.
  • Always provide both scriptTrue and scriptFalse to properly reverse behaviors.
  • Clear messages in scriptFalse to avoid stale messages.
  • Set isolateScript: true to avoid variable conflicts.

Common g_form methods:

g_form.setVisible('field_name', true);
g_form.setMandatory('field_name', true);
g_form.setReadOnly('field_name', true);
g_form.setValue('field_name', 'value');
g_form.getValue('field_name');
g_form.clearValue('field_name');
g_form.addInfoMessage('message');
g_form.addErrorMessage('message');
g_form.showFieldMsg('field_name', 'text', 'info');
g_form.hideFieldMsg('field_name');
g_form.clearMessages();

Example with scripts:

import { UiPolicy } from '@servicenow/sdk/core';

export const validationPolicy = UiPolicy({
$id: Now.ID['incident_validation_policy'],
table: 'incident',
shortDescription: 'Validate fields based on priority',
onLoad: true,
conditions: 'priority=1',
runScripts: true,
uiType: 'all',
isolateScript: true,
scriptTrue: `function onCondition() {
g_form.setMandatory('justification', true);
g_form.setMandatory('business_service', true);
g_form.showFieldMsg('priority', 'High priority requires justification', 'info');
if (g_form.getValue('urgency') == '') {
g_form.setValue('urgency', '1');
}
}`,
scriptFalse: `function onCondition() {
g_form.setMandatory('justification', false);
g_form.setMandatory('business_service', false);
g_form.hideFieldMsg('priority');
}`,
});

Related list actions control visibility of related lists on forms. The list property identifies which related list to control:

  • GUID format (system relationships): "b9edf0ca0a0a0b010035de2d6b579a03" -- plugin auto-adds REL: prefix.
  • Table.Field format (reference fields): "incident.caller_id"
  • Table.Table format (parent-child): "change_request.change_task"

Finding related list GUIDs:

var gr = new GlideRecord('sys_ui_related_list');
gr.addQuery('name', 'CONTAINS', 'your_table_name');
gr.query();
while (gr.next()) {
gs.print(gr.name + ' -> ' + gr.sys_id);
}

UI Formatters

Formatters add non-field content to forms (activity streams, process flows, checklists). Custom formatters are not supported in Fluent -- always use built-in formatters.

Built-In Formatters

FormatterMacroWhen to UsePosition
Activityactivity.xmlJournal entries, comments, work notesLast in section
Process Flowprocess_flowLifecycle stage visualizationFirst in section
CI Relationsui_ng_relation_formatter.xmlCMDB relationship mapsFirst in section
Parent Breadcrumbparent_crumbsParent hierarchy trailFirst in section
Contextual Searchcxs_table_search.xmlAuto-suggest knowledge articlesBelow search context field
Variables Editorcom_glideapp_questionset_default_question_editorRecord producer variables--
Checklistinline_checklist_macroSub-task trackingLast in section
Attached Knowledgeattached_knowledgeLinked knowledge articlesLast in section

Key Rules

  1. Activity and Attached Knowledge formatters already exist globally -- never create sys_ui_formatter records for them. Skip straight to adding the sys_ui_element.
  2. A formatter requires a section -- it must reside in a sys_ui_section, and the section must have at least one non-formatter element.
  3. Parent Breadcrumb requires a field named exactly parent -- no variations like parent_task or parent_record.
  4. Process Flow requires stage configuration -- verify sys_process_flow records exist for the target table.
  5. Position matters -- Process Flow and Parent Breadcrumb go first; Activity and Checklist go last.

Sequential Steps to Add a Formatter

  1. Check formatter exists (sys_ui_formatter): For Activity and Attached Knowledge, skip to step 4. For others, query sys_ui_formatter for the target table, then global, then the extended-from table.
  2. If Process Flow: Verify/create stage records in sys_process_flow.
  3. If Contextual Search: Verify/create search config in cxs_table_config.
  4. Check section exists (sys_ui_section): Query for the target table and view. Create if missing.
  5. Add formatter element (sys_ui_element): Create with type: 'formatter' and reference to the section and formatter.
  6. Ensure at least one non-formatter element exists in the section.

Formatter Examples

Add Activity Formatter (no formatter record needed)

import { Record } from '@servicenow/sdk/core';

Record({
$id: Now.ID['activity_formatter_element'],
table: 'sys_ui_element',
data: {
sys_ui_section: section.$id,
element: 'activity.xml',
type: 'formatter',
position: 99,
},
});

Create Process Flow Formatter

import { Record } from '@servicenow/sdk/core';

export const processFlowFormatter = Record({
$id: Now.ID['process_flow_formatter'],
table: 'sys_ui_formatter',
data: {
name: 'Process Flow Formatter',
type: 'formatter',
formatter: 'process_flow.xml',
table: 'table_name',
active: true,
},
});

Add stages for Process Flow

import { Record } from '@servicenow/sdk/core';

Record({
$id: Now.ID['flow-stage-new'],
table: 'sys_process_flow',
data: {
active: true,
condition: 'state=new^EQ',
label: 'New',
name: 'Task Flow - New State',
order: '100',
table: 'table_name',
},
});

Record({
$id: Now.ID['flow-stage-progress'],
table: 'sys_process_flow',
data: {
active: true,
condition: 'state=in_progress^EQ',
label: 'In Progress',
name: 'Task Flow - In Progress',
order: '200',
table: 'table_name',
},
});