Skip to main content
Version: 4.10.1

View Rules

Guide for creating ServiceNow View Rules using the Record API. View rules automatically switch users to a specific view based on conditions, device type, or custom scripts — unlike manual view selection from the dropdown.

When to Use​

  • Automatically switching to a mobile-optimized view on mobile devices
  • Showing a specialized view for high-priority or critical records
  • Routing users to different views based on their role (via advanced script)
  • Overriding the user's manually selected view when conditions are met
  • Combining device type and record conditions for targeted view selection

Instructions​

  1. Create the target view first: The view referenced by data.view must exist in sys_ui_view. Create it using a Record({ table: 'sys_ui_view', ... }) before the view rule.
  2. Use the view name, not title: The data.view property must use the view's name field, not its title.
  3. End conditions with ^EQ: All encoded query conditions in data.condition must terminate with ^EQ.
  4. Set evaluation order: Lower order values are evaluated first. Use 100 as default, lower for higher priority rules.
  5. Choose condition vs advanced script: Use data.condition for simple field-based matching. Use data.advanced: true with a script for complex logic (role checks, multi-table queries).
  6. Consider overrides_user_preference: When true (default), the rule overrides the user's manual view selection. Set to false if the user's choice should take priority.

Key Concepts​

View rules are created using the Record() API with table: 'sysrule_view'. See the record-api topic for the Record wrapper ($id, table, data, $meta). The compiler validates properties at build time.

Key Properties (set inside data)​

PropertyTypeRequiredDescription
namestringYesDescriptive name for the rule.
tablestringYesTarget table where the rule applies.
viewstringConditionalView name from sys_ui_view. Required unless using advanced script.
conditionstringNoEncoded query ending with ^EQ for simple field-based matching.
match_conditionsstringNo'ALL' (default) or 'ANY'. Controls whether all or any conditions must match. These are the platform-recognized values (sys_choice); the column accepts any string at the type level.
device_typestringNo'browser' (default), 'mobile', or 'tablet'. These are the platform-recognized values (sys_choice); the column accepts any string at the type level.
activebooleanNoWhether the rule is active. Default: true.
advancedbooleanNoSet true to enable custom script mode instead of condition-based matching.
scriptstringNoJavaScript for complex logic (when advanced: true). Must set the answer variable. If advanced: false, omit or set script: '' — the schema default IIFE is otherwise silently persisted to the record.
overrides_user_preferencebooleanNoWhen true (default), overrides the user's manual view selection.
ordernumberNoEvaluation order (lower = first). Default: 100.

Warning: Never set sys_id in data: — let the SDK manage record identity via $id.

sysrule_view has no build-plugin (only sys_ui_view is registered in view-plugin.ts), so there is no coalesce key. Use stable $id values (e.g., Now.ID['my-rule-name']) to prevent duplicate view rules on redeploy.

For the full field schema (types, defaults, maxLengths), refer to the shipped SDK schema definition at @servicenow/sdk → tables/sysrule_view.now.ts, or query table sys_dictionary where name=sysrule_view, retrieving element,column_label,internal_type,max_length,mandatory (see the query topic).

Condition Syntax​

Conditions use ServiceNow encoded query syntax and must end with ^EQ:

priority=1^EQ -- Priority is Critical
priority=1^ORpriority=2^EQ -- Priority is Critical OR High
state!=6^category=hardware^EQ -- Not Resolved AND Hardware category

Common operators: =, !=, >, <, >=, <=, LIKE, STARTSWITH, ISEMPTY, ISNOTEMPTY, ^ (AND), ^OR (OR).

Common Mistakes in Conditions​

MistakeProblemSolution
Wrong casingPriority=1^EQUse backend name: priority=1^EQ
Using labelsstate=Resolved^EQUse value: state=6^EQ
Missing ^EQpriority=1Always end with: priority=1^EQ

Finding Backend Names​

To find a field's backend name, query table sys_dictionary where name=<table>^element=<field>, retrieving fields element,column_label (see the query topic).

Example: table incident, field priority.

Finding Choice Values​

To find choice field values, query table sys_choice where name=<table>^element=<field>, retrieving fields label,value (see the query topic).

Example: table incident, field state.

Advanced Scripts​

When data.advanced: true, the script must follow this IIFE structure:

script: `(function overrideView(view, is_list) {
// view (string) - Current view name
// is_list (boolean) - true for list, false for form
// current - Current record (forms only, undefined for lists)
// answer - Set to view name (string) or null

answer = 'my_view_name';
})(view, is_list);`
  • Set answer to the view name (not title) or null to skip
  • current is only available for forms, not lists
  • Always wrap in an IIFE with (view, is_list) parameters

match_conditions​

Controls how multiple conditions are evaluated:

  • 'ALL' (default): All conditions must be true (AND logic)
  • 'ANY': At least one condition must be true (OR logic)

Use 'ANY' when you want the rule to trigger if any of several conditions are met.

Evaluation Order​

View rules are evaluated in this 7-step sequence:

  1. Active check: Only active rules (active: true) are considered
  2. Device type match: If device_type is set, it must match the user's device
  3. Condition evaluation: If condition is set, it must evaluate to true
  4. Match conditions: If multiple conditions exist, match_conditions determines AND/OR logic
  5. Script execution: If advanced: true, the script runs and sets answer
  6. First match wins: The first rule that passes all checks is applied
  7. User preference override: If overrides_user_preference: false, user's manual selection takes priority

Lower order values are evaluated first within each step.

One Advanced Rule Per Device Type​

Important: When multiple advanced rules target the same table + device_type combination, only the rule with the lowest order value executes. Other rules are ignored.

Best practice: Combine all logic for a given table/device into a single advanced script instead of creating multiple advanced rules.

Script Variables​

Available variables in advanced scripts:

VariableTypeScopeDescription
viewstringAllCurrent view name
is_listbooleanAlltrue for lists, false for forms
currentGlideRecordForms onlyCurrent record (undefined for lists)
answerstring | nullAllSet to target view name or null
gsGlideSystemAllServer-side API access

GlideElement Methods​

When accessing current record fields, use these methods:

  • .toString() — Returns field value as string (e.g., current.priority.toString())
  • .nil() — Checks if field is empty (e.g., current.assigned_to.nil())
  • .getDisplayValue() — Returns display value for reference fields
  • .getValue() — Returns raw value

Always use .toString() for comparisons: current.priority.toString() === "1"

Debugging​

Add logging to troubleshoot view rule execution:

script: `(function overrideView(view, is_list) {
gs.info('View Rule Debug: view=' + view + ', is_list=' + is_list);

if (!is_list && typeof current !== 'undefined') {
gs.info('Current record priority: ' + current.priority.toString());
}

answer = 'my_view';
})(view, is_list);`

Use gs.info() for logging and wrap logic in try/catch for error handling:

script: `(function overrideView(view, is_list) {
try {
gs.info('View Rule Debug: view=' + view + ', is_list=' + is_list);

var user = gs.getUser();
gs.info('User: ' + gs.getUserName() + ', Has admin: ' + user.hasRole('admin'));

if (user.hasRole('admin')) {
answer = 'admin_view';
} else {
answer = null;
}
} catch (e) {
gs.error('View Rule Error: ' + e.message);
answer = null;
}
})(view, is_list);`

Best practice: Always use try/catch in production view rules to prevent script errors from breaking form rendering. Set answer = null in the catch block to fall back to the default view.

Common Mistakes​

Avoid these frequent errors in view rule scripts:

  1. current unchecked: May be undefined → check !is_list && typeof current !== 'undefined'
  2. answer not set: Rule won't work → always set answer = 'view_name' or null
  3. Missing .toString(): Comparison fails → use current.priority.toString() === "1"
  4. Syntax errors: Script fails → fix quotes, semicolons, syntax
  5. Using view title: View not found → use view name field, not title
  6. Heavy GlideRecord queries: Slows performance → use current or cached data
  7. Wrong comparison operators: Use === for equality, not =

Pre-Creation Checklist​

Before creating any view rule, validate:

  1. Verify target view exists — query sys_ui_view with name=<view_name> to confirm the view is created
  2. Confirm backend names — all field names and values must be backend names from sys_dictionary / sys_choice, not UI labels
  3. Verify ^EQ termination — every encoded query condition must end with ^EQ
  4. Verify roles exist (for advanced scripts) — if the script uses gs.hasRole('role_name') or user.hasRole('role_name'), query sys_user_role with name=<role> to confirm the role exists before referencing it

Performance Best Practices​

  1. Minimize database queries — use the current record instead of new GlideRecord lookups
  2. Cache user info — use gs.getUser() (cached per request), not repeated queries
  3. Early returns — exit the script as soon as a condition is met
  4. Check context first — always verify !is_list && typeof current !== 'undefined' before accessing current
  5. Use session properties — prefer session data over repeated property lookups

Examples​

Mobile-First with Fallback — View, Form, List, and Rule​

Create a mobile-optimized experience: a lightweight view, a compact form, a slim list, and a view rule that auto-routes mobile users. Desktop users see the default view unaffected.

import '@servicenow/sdk/global'
import { Record, Form, List, default_view } from '@servicenow/sdk/core'

// Prerequisite: queried sys_ui_view where name='x_myapp_mobile'^ORtitle='Mobile View' — 0 results
const mobileView = Record({
$id: Now.ID['task-mobile-view'],
table: 'sys_ui_view',
data: { name: 'x_myapp_mobile', title: 'Mobile View' },
})

Form({
table: 'x_myapp_task',
view: mobileView,
sections: [
{
caption: 'Task',
content: [
{
layout: 'one-column',
elements: [
{ field: 'short_description', type: 'table_field' },
{ field: 'state', type: 'table_field' },
{ field: 'assigned_to', type: 'table_field' },
],
},
],
},
],
})

List({
table: 'x_myapp_task',
view: mobileView,
columns: [
{ element: 'number' },
{ element: 'short_description' },
{ element: 'state' },
],
})

Record({
$id: Now.ID['task-mobile-rule'],
table: 'sysrule_view',
data: {
name: 'Auto-Route Mobile Users',
table: 'x_myapp_task',
view: 'x_myapp_mobile',
device_type: 'mobile',
active: true,
overrides_user_preference: true,
},
})

Priority-Based Triage — Condition Rule with Form​

High-priority records automatically switch to a triage view that surfaces escalation fields. Lower-priority records continue to use the default view. The order property controls which rule wins when multiple rules match.

import '@servicenow/sdk/global'
import { Record, Form } from '@servicenow/sdk/core'

// Prerequisite: queried sys_ui_view where name='x_myapp_triage'^ORtitle='Triage View' — 0 results
const triageView = Record({
$id: Now.ID['triage-view'],
table: 'sys_ui_view',
data: { name: 'x_myapp_triage', title: 'Triage View' },
})

Form({
table: 'x_myapp_task',
view: triageView,
sections: [
{
caption: 'Triage',
content: [
{
layout: 'two-column',
leftElements: [
{ field: 'short_description', type: 'table_field' },
{ field: 'priority', type: 'table_field' },
{ field: 'state', type: 'table_field' },
],
rightElements: [
{ field: 'assigned_to', type: 'table_field' },
{ field: 'escalation', type: 'table_field' },
{ field: 'assignment_group', type: 'table_field' },
],
},
],
},
],
})

Record({
$id: Now.ID['triage-rule'],
table: 'sysrule_view',
data: {
name: 'Triage High-Priority Tasks',
table: 'x_myapp_task',
view: 'x_myapp_triage',
condition: 'priority=1^ORpriority=2^EQ',
device_type: 'browser',
active: true,
overrides_user_preference: true,
order: 50,
},
})

Multi-Persona Routing — Advanced Script with Multiple Views​

Route users to different views based on their role. Admins get a full dashboard, ITIL agents get an operational view, and everyone else stays on the default. Uses an advanced script with IIFE wrapper — the only option when logic requires role checks or multi-table queries.

import '@servicenow/sdk/global'
import { Record, Form, default_view } from '@servicenow/sdk/core'

// Prerequisite: queried sys_ui_view where name='x_myapp_admin'^ORtitle='Admin View' — 0 results
const adminView = Record({
$id: Now.ID['admin-view'],
table: 'sys_ui_view',
data: { name: 'x_myapp_admin', title: 'Admin View', roles: ['admin'] },
})

// Prerequisite: queried sys_ui_view where name='x_myapp_agent'^ORtitle='Agent View' — 0 results
const agentView = Record({
$id: Now.ID['agent-view'],
table: 'sys_ui_view',
data: { name: 'x_myapp_agent', title: 'Agent View', roles: ['itil'] },
})

Form({
table: 'x_myapp_task',
view: adminView,
sections: [
{
caption: 'Admin Dashboard',
content: [
{
layout: 'two-column',
leftElements: [
{ field: 'number', type: 'table_field' },
{ field: 'priority', type: 'table_field' },
],
rightElements: [
{ field: 'escalation', type: 'table_field' },
{ field: 'assignment_group', type: 'table_field' },
],
},
],
},
],
})

Form({
table: 'x_myapp_task',
view: agentView,
sections: [
{
caption: 'Work Details',
content: [
{
layout: 'one-column',
elements: [
{ field: 'short_description', type: 'table_field' },
{ field: 'state', type: 'table_field' },
{ field: 'assigned_to', type: 'table_field' },
{ field: 'work_notes', type: 'table_field' },
],
},
],
},
],
})

Record({
$id: Now.ID['role-routing-rule'],
table: 'sysrule_view',
data: {
name: 'Route by User Role',
table: 'x_myapp_task',
advanced: true,
active: true,
device_type: 'browser',
overrides_user_preference: true,
script: `(function overrideView(view, is_list) {
var user = gs.getUser();
if (user.hasRole('admin')) {
answer = 'x_myapp_admin';
} else if (user.hasRole('itil')) {
answer = 'x_myapp_agent';
} else {
answer = null;
}
})(view, is_list);`,
},
})

Complex Conditional Form Logic​

Multi-field check on form records with proper current guard. Demonstrates checking multiple fields and setting different views based on combined conditions.

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

Record({
$id: Now.ID['complex-conditional-rule'],
table: 'sysrule_view',
data: {
name: 'Complex Conditional Rule',
table: 'incident',
advanced: true,
device_type: 'browser',
active: true,
script: `(function overrideView(view, is_list) {
// CRITICAL: Check if form and current exists
if (!is_list && typeof current !== 'undefined') {
var priority = current.priority.toString();
var state = current.state.toString();

// Multi-field logic
if (priority === '1' && state === '2') {
answer = 'critical_active_view';
} else if (priority === '1' && state === '7') {
answer = 'critical_closed_view';
} else {
answer = null; // Use default
}
} else {
answer = null;
}
})(view, is_list);`,
},
})

Key points:

  • Always check !is_list && typeof current !== 'undefined' before accessing current
  • Use .toString() for field comparisons
  • Set answer = null to use default view
  • Guard against list context where current is undefined

Avoidance​

  • Never omit ^EQ from conditions — encoded queries in view rules must end with ^EQ or they will not evaluate correctly.
  • Never use title as the view reference — always use the view's name field in data.view and in advanced scripts.
  • Never create a view rule without creating the target view first — the view must exist in sys_ui_view.
  • Never access current in list context — current is only available for form view rules, not list view rules. Check is_list in advanced scripts.
  • Never forget the IIFE wrapper in advanced scripts — the script must be wrapped in (function overrideView(view, is_list) { ... })(view, is_list);.
  • Never use view rules for access control — view rules switch layouts, not restrict access. Use roles/group/user on sys_ui_view for access control.
  • Never create multiple advanced rules for the same table + device type — only the rule with the lowest order value executes. Combine all logic into a single advanced script.
  • Never use field labels in encoded query conditions — use backend names from sys_dictionary. Query table sys_dictionary where name=<table>^element=<field>, retrieving element (see the query topic).
  • Never access current without checking !is_list && typeof current !== 'undefined' — current may be undefined in list context or during evaluation.
  • Never use Business Rules, Client Scripts, or UI Policies for view switching — always use View Rules (sysrule_view) for automatic view routing.

See​

  • See the view-guide topic for full documentation on creating views, including uniqueness checks, access control patterns, and the default_view import.