Skip to main content
Version: Latest (4.9.0)

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/sdktables/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.