Skip to main content
Version: 4.10.1

Views

Guide for creating ServiceNow UI Views using the Record API. Views define custom layout variants for forms and lists, controlling which fields/columns are shown and who can access the layout.

Uniqueness check - Mandatory​

Do NOT write any Record({ table: 'sys_ui_view' }) call until you have confirmed 0 results from a uniqueness query. name is the coalesce key — duplicate names silently overwrite existing views and destroy their group/user/roles fields on deploy. title collisions cause UX confusion (duplicate entries in the view selector dropdown) but not coalesce-level data loss.

For each view, query sys_ui_view where name=<proposed_name>^ORtitle=<proposed_title>, retrieving sys_id,name,title,sys_scope (see the query topic).

  • 0 results → safe to proceed
  • >0 results → change both name and title, re-query until 0

NEVER use nameLIKE, name= alone, or omit title — all three miss collisions.

When to Use​

  • Creating a custom form layout different from the default (e.g., mobile view, admin dashboard)
  • Restricting a layout to specific roles (e.g., ITIL agents, admins)
  • Creating a hidden view for portal, API, or mobile app use only
  • Providing group-specific or user-specific form/list layouts
  • Defining a view that can be shared across Form() and List() calls

View names may contain letters, digits, underscores, and commas (/^[a-zA-Z0-9_,]+$/). No spaces or special characters.

Do not create a view for the standard/default layout — use default_view instead.

Instructions​

  1. Check if you need a custom view: If the user wants the default layout, import default_view from @servicenow/sdk/core and skip view creation entirely.
  2. Verify uniqueness (mandatory, per view): Query sys_ui_view where name=<proposed_name>^ORtitle=<proposed_title>, retrieving sys_id,name,title,sys_scope (see the query topic).
    • 0 results → proceed
    • >0 results → change both name and title, re-query until 0
  3. Choose the access pattern: Public (no restrictions), role-based, group-based, user-specific, or hidden.
  4. Verify roles exist (for role-based views): Query table sys_user_role where name=<role>, retrieving fields sys_id,name (see the query topic). All referenced roles must exist.
  5. Create the view Record: Use Record({ table: 'sys_ui_view', ... }) and export as a const.
  6. Use the view const: Pass the same const to Form({ view: myView }) and/or List({ view: myView }).
  7. Complete the solution: A view alone is non-functional — always create the form/list components that use it.

Choosing the Right Approach​

Before creating a view, determine if a view is the right solution for your use case:

Views vs View Rules vs UI Policies vs List Controls — choose based on scope:

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 — choose based on intent:

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

View Type Selection​

Use this table to determine the correct view type from user intent. Match by meaning, not exact keywords.

User Intent SignalsView TypeAction
"simple", "default", "basic", "normal"Default Viewimport { default_view } from '@servicenow/sdk/core' — do not create a view Record
"admins", "ITIL", "role", generic function names (e.g., "managers", "agents") — title prefixes like CRO/CEO/CFO alone do NOT make it role-based if a specific person's name followsRole-basedSet roles array with role name strings
"team", "department", "group", "assignment group", org-unit names — the word "group" after any term signals Group-basedGroup-basedQuery sys_user_group for sys_id, set group
"portal", "hidden", "API only", "not in dropdown", "mobile only"HiddenSet hidden: true
Named individual ("John", "Dr. Smith", "CEO Alice", "jane.admin") — a specific person's name is the signal, not the title prefixUser-specificQuery sys_user for sys_id, set user
"everyone", "all users", "public", "no restrictions"PublicOmit all access fields (roles, group, user, hidden)

Key Concepts​

Views are created using the Record() API with table: 'sys_ui_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
namestringLogically requiredTechnical identifier. This is the coalesce key — duplicate names silently overwrite. Not schema-mandatory (mandatory: false), but required for coalesce.
titlestringLogically requiredDisplay name shown in the view selector dropdown. Not schema-mandatory, but recommended to avoid blank entries. Duplicates cause UX confusion but not data loss.
rolesstring[]NoRole name strings to restrict access (e.g., ['admin', 'itil']). UserRolesColumn — stored comma-joined, ≤100 chars total.
groupstring | Record<'sys_user_group'>Nosys_id string or Record reference of a sys_user_group to restrict to group members.
userstring | Record<'sys_user'>Nosys_id string or Record reference of a sys_user to restrict to a single user.
hiddenbooleanNoIf true, hidden from platform view selector (portal/API/mobile use only).

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

Access Control Patterns​

PatternProperties SetWho Can Access
PublicNone (no roles/user/group/hidden)Everyone
Role-basedroles: ['admin', 'itil']Users with specified roles
Group-basedgroup: '<group_sys_id>'Members of the group
User-specificuser: '<user_sys_id>'Only that specific user
Hiddenhidden: trueNot in dropdown — portal/API/mobile only. Warning: hidden is a UI visibility control only — it does NOT provide security. Admin users can still see and select hidden views. Always use roles, ACLs, and widget permissions for actual access control

Combining Hidden with Access Control​

hidden: true can be combined with roles, group, or user for layered control — hidden from the dropdown AND restricted to specific users:

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

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

Default View​

For the standard layout, use default_view — no custom Record needed:

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

Form({ table: 'x_myapp_task', view: default_view, sections: [...] })

Examples​

Complete Table Setup — View, Form, and List​

Create a mobile view and wire it to both a compact form and a minimal list. A view alone is non-functional — always pair it with form/list components.

import '@servicenow/sdk/global'
import { Record, Form, List } 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['mobile-view'],
table: 'sys_ui_view',
data: {
name: 'x_myapp_mobile',
title: 'Mobile View',
},
})

Form({
table: 'x_myapp_task',
view: mobileView,
sections: [
{
caption: 'Details',
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' },
],
})

Multi-Persona Application — Admin vs Agent​

Two views serve different audiences. The admin dashboard shows escalation fields and activity; the default view is a streamlined agent form. Each view drives its own form layout.

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

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

Form({
table: 'x_myapp_task',
view: adminView,
sections: [
{
caption: 'Overview',
content: [
{
layout: 'two-column',
leftElements: [
{ field: 'number', type: 'table_field' },
{ field: 'state', type: 'table_field' },
{ field: 'priority', type: 'table_field' },
],
rightElements: [
{ field: 'assigned_to', type: 'table_field' },
{ field: 'assignment_group', type: 'table_field' },
{ field: 'escalation', type: 'table_field' },
],
},
],
},
{
caption: 'Activity',
content: [
{
layout: 'one-column',
elements: [
{ type: 'formatter', formatterRef: 'Activities_Filtered' },
],
},
],
},
],
})

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

List({
table: 'x_myapp_task',
view: adminView,
columns: [
{ element: 'number' },
{ element: 'short_description' },
{ element: 'state' },
{ element: 'priority' },
{ element: 'assigned_to' },
{ element: 'escalation' },
],
})

Hidden View with View Rule — Portal Auto-Routing​

Create a hidden view for portal/API consumers, then add a view rule so mobile users are automatically switched to it. Hidden views don't appear in the dropdown but are fully functional when accessed programmatically.

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

// Prerequisite: queried sys_ui_view where name='x_myapp_service_portal'^ORtitle='Service Portal View' — 0 results
const portalView = Record({
$id: Now.ID['portal-view'],
table: 'sys_ui_view',
data: {
name: 'x_myapp_service_portal',
title: 'Service Portal View',
hidden: true,
},
})

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

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

Record({
$id: Now.ID['portal-mobile-rule'],
table: 'sysrule_view',
data: {
name: 'Auto-Route Mobile to Portal View',
table: 'x_myapp_task',
view: 'x_myapp_service_portal',
device_type: 'mobile',
active: true,
overrides_user_preference: true,
},
})

Group-Based View​

Create a view restricted to members of a specific group. Always verify the group exists before creating the view.

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

// Verify group exists first: query table `sys_user_group` where `name=Facilities^active=true`, retrieving fields `sys_id,name` (see the `query` topic)

// Option A: Record reference (recommended)
const facilitiesGroup = Record({
$id: Now.ID['facilities-group'],
table: 'sys_user_group',
data: {
name: 'x_myapp_facilities',
description: 'Facilities Team',
active: true,
},
})

// Prerequisite: queried sys_ui_view where name + title — 0 results
const facilitiesView = Record({
$id: Now.ID['facilities-view'],
table: 'sys_ui_view',
data: {
name: 'x_myapp_facilities',
title: 'Facilities View',
group: facilitiesGroup, // Record reference
},
})

// Option B: sys_id string fallback (when group already exists on instance)
// Prerequisite: queried sys_ui_view where name + title — 0 results
const facilitiesViewBySysId = Record({
$id: Now.ID['facilities-view-sysid'],
table: 'sys_ui_view',
data: {
name: 'x_myapp_facilities_alt',
title: 'Facilities View (Alt)',
group: '<group_sys_id>', // sys_id from query result
},
})

// Use the view in Form or List
Form({
table: 'x_myapp_task',
view: facilitiesView,
sections: [
{
caption: 'Task Details',
content: [
{
layout: 'one-column',
elements: [
{ field: 'short_description', type: 'table_field' },
{ field: 'assigned_to', type: 'table_field' },
],
},
],
},
],
})

Access: Only members of the specified group can see and select this view.

User-Specific View​

Create a view restricted to a single user. Always verify the user exists before creating the view.

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

// Verify user exists first: query table `sys_user` where `user_name=jane.doe^active=true`, retrieving fields `sys_id,user_name` (see the `query` topic)

// Option A: Record reference (recommended)
const newUser = Record({
$id: Now.ID['user-jane'],
table: 'sys_user',
data: {
user_name: 'jane.smith',
first_name: 'Jane',
last_name: 'Smith',
email: 'jane.smith@example.com',
active: true,
},
})

// Prerequisite: queried sys_ui_view where name + title — 0 results
const janePersonalView = Record({
$id: Now.ID['jane-personal-view'],
table: 'sys_ui_view',
data: {
name: 'incident_jane_smith_personal',
title: "Jane Smith's Personal View",
user: newUser, // Record reference
},
})

// Option B: sys_id string fallback (when user already exists on instance)
// Prerequisite: queried sys_ui_view where name + title — 0 results
const janeView = Record({
$id: Now.ID['jane-view'],
table: 'sys_ui_view',
data: {
name: 'incident_jane_personal',
title: "Jane's Personal View",
user: '<user_sys_id>', // sys_id from query result
},
})

// Use the view in Form or List
Form({
table: 'incident',
view: janePersonalView,
sections: [
{
caption: 'My Incidents',
content: [
{
layout: 'two-column',
leftElements: [
{ field: 'number', type: 'table_field' },
{ field: 'short_description', type: 'table_field' },
],
rightElements: [
{ field: 'priority', type: 'table_field' },
{ field: 'state', type: 'table_field' },
],
},
],
},
],
})

Access: Only the specified user can see and select this view.

Avoidance​

  • Never create a view Record for the default layout — use import { default_view } from '@servicenow/sdk/core' instead.
  • Never use nameLIKE<prefix> to check view uniqueness — it misses title collisions. Always query with name=<exact>^ORtitle=<exact> per the "Uniqueness check - Mandatory" section.
  • Never create a view without accompanying form/list components — a view alone is non-functional.
  • Never create ACLs for view access — use the roles/group/user fields on sys_ui_view. ACLs are for table/record/field security.
  • Never use title as the view reference — always use name when referencing views in view rules or other configurations.
  • Never create many user-specific views when role-based views would work — prefer roles for maintainability and performance.

See​

  • See the view-rule-guide topic for automatic view switching based on conditions, device type, or user roles.