Skip to main content
Version: 4.9.0

Form Formatters

Guide for adding formatters to ServiceNow forms using the Form API. Formatters display special UI components (activity streams, process flows, knowledge panels, checklist, parent breadcrumb) that are not regular table fields.

When to Use

Use formatters to enhance existing forms with these capabilities:

  • Adding an activity stream (comments, work notes, journal) to an existing form
  • Displaying attached knowledge articles on existing task records
  • Showing a process flow (lifecycle stages) at the top of an existing form
  • Adding a checklist for tracking sub-tasks on an existing form
  • Displaying CI relationship maps on existing CMDB record forms
  • Showing parent breadcrumb navigation on existing hierarchical task records
  • Surfacing contextual search results (knowledge articles, catalog items) on an existing form based on field values
  • Displaying variable/question values from record producers on existing service catalog forms

Critical Instructions

When adding formatter elements to forms, follow these guidelines to ensure proper functionality. Failure to follow these requirements may result in build or runtime errors.

  1. Always set type: 'formatter' — this is required for the element to be treated as a formatter
  2. formatterRef is required — use a predefined key string (e.g., 'Activities_Filtered'), a Formatter.* constant, or a Record<'sys_ui_formatter'> reference
  3. formatterName is optional — automatically derived for predefined Formatter.* keys and Record<'sys_ui_formatter'> references
  4. Import Formatter from @servicenow/sdk/core when using formatter constants
  5. Never create custom formatter macros — not supported in Fluent. Always use built-in formatters
  6. Never create new sys_ui_formatter records for globally available formatters (Activity, Attached Knowledge) — they already exist
  7. Formatter properties are formatter-only — never use formatterRef or formatterName on other element types
  8. Always include angular_module when required — Checklist and CI Relations formatters require this property

Instructions

  1. Identify the formatter: Determine which formatter type matches your use case from the Built-in Formatters table below.
  2. Check availability: Activity and Attached Knowledge exist globally — skip to step 4. For all others, query sys_ui_formatter using the 3-level cascade (target table → global → parent table).
  3. Create if missing: If the sys_ui_formatter record does not exist, create one using Record() before the Form() call.
  4. Add the element: Add { type: 'formatter', formatterRef: ... } to the appropriate section in your Form() call.
  5. Position correctly: Follow the placement rules — visual formatters go at the top, historical formatters go in dedicated sections at the bottom.
  6. Handle prerequisites: Some formatters require additional setup (stage records, CXS config, parent field). See per-formatter details below.

API Reference

See the form-api topic for the full FormElement type reference, including formatterRef and formatterName properties.

Table Schema

The sys_ui_formatter table schema for creating formatter records using Record():

FieldTypeRequiredDescription
nameStringYesDisplay name of the formatter (shown in form designer and admin lists).
typeStringYesMust be 'formatter' — identifies this as a formatter record.
formatterStringYesMacro file name (e.g., 'process_flow.xml', 'activity.xml', 'inline_checklist_macro.xml'). Must match an existing platform formatter macro — never create custom macros.
tableStringYesTarget table name. Use 'global' for all tables, specific table name (e.g., 'incident'), or parent table (e.g., 'task', 'cmdb_ci') for inheritance.
activeBooleanYesWhether the formatter is active and available for use. Set to true to enable.
angular_moduleStringConditionalAngularJS module name. Required for Checklist ('inlineChecklist') and CI Relations ('sn.ciRelationFormatter'). Optional for Contextual Search ('sn.cxs.table_search'). Omit for all other formatters.

Key Concepts

Form API Structure

Understanding the Form API hierarchy is essential for correct formatter placement:

Form Structure:

  • A form has multiple sections (array of section objects)
  • Each section has a content array containing layout blocks
  • Layout blocks can be one-column (with elements array) or two-column (with leftElements and rightElements arrays)
  • Element position is determined by array order within the layout block

Example structure:

Form({
table: 'x_myapp_task',
view: default_view,
sections: [ // Array of sections
{
caption: 'Overview',
content: [ // Array of layout blocks
{
layout: 'one-column',
elements: [ // Array of elements - order matters!
// First element (index 0)
{ type: 'formatter', formatterRef: processFlowFormatter },
// Second element (index 1)
{ field: 'name', type: 'table_field' },
],
},
],
},
],
})

When we say "first element in section", this means index 0 in the elements array of the first layout block.

Formatter Properties

PropertyRequiredTypeDescription
typeYes'formatter'Must be 'formatter'
formatterRefYesFormatter.* constant, Record<'sys_ui_formatter'>, or key stringIdentifies which formatter to display
formatterNameNostringMacro name (e.g., 'activity.xml'). Auto-derived for Formatter.* constants and Record references

Formatter Constants

Only two formatters have predefined constants (import from @servicenow/sdk/core):

import { Formatter } from '@servicenow/sdk/core'
ConstantMacroDescription
Formatter.Activities_Filteredactivity.xmlActivity stream with filtering (comments, work notes, journal)
Formatter.Attached_Knowledgeattached_knowledgeRelated knowledge articles panel

All other formatters require a Record<'sys_ui_formatter'> reference as formatterRef.

Built-in Formatters

FormatterMacroAngular ModulePositionPrerequisites
Activityactivity.xmlN/ADedicated section at bottomNone — globally available
Attached Knowledgeattached_knowledgeN/ADedicated section at bottomKnowledge Management plugin; table extends task
Process Flowprocess_flow.xmlN/AFirst element in first sectionsys_process_flow stage records for the table
Checklistinline_checklist_macro.xmlinlineChecklistDedicated section near bottomChecklist plugin; task-derived table
CI Relationsui_ng_relation_formatter.xmlsn.ciRelationFormatterFirst element in sectionCMDB plugin; table extends cmdb_ci; relationship records in cmdb_rel_ci
Parent Breadcrumbparent_crumbs.xmlN/AFirst element in sectionTable must have a field named exactly parent (reference type)
Contextual Searchcxs_table_search.xmlsn.cxs.table_searchAfter the search context fieldCXS enabled; cxs_table_config and cxs_table_field_config records
Variables Editorcom_glideapp_questionset_default_question_editorN/ANo fixed positionTable extends task; record created from record producer

Angular Module Requirement:

  • Checklist: MUST include angular_module: 'inlineChecklist' in the sys_ui_formatter Record() data
  • CI Relations: MUST include angular_module: 'sn.ciRelationFormatter' in the sys_ui_formatter Record() data
  • Contextual Search: The table lists sn.cxs.table_search but this may be optional — verify on your instance
  • All others: No angular_module required

Placement Rules

CategoryFormattersWhere to PlaceAdditional Guidance
Visual/OverviewProcess Flow, Parent Breadcrumb, CI RelationsFirst element in the main sectionThese are visual headers that belong with record details — place in a section that also contains fields
ContextualContextual Search, Variables EditorSame section as related fields, after themPlace immediately after the field they relate to (e.g., CXS after short_description)
Historical/ReferenceActivity, Checklist, Attached KnowledgeDedicated section at bottom of formFormatter-only sections (no other fields) — these become separate tabs in the UI

General rules:

  1. Use one-column layout for formatters — formatters typically span the full width; avoid placing them in leftElements or rightElements of two-column layouts
  2. One formatter per dedicated section — Activity, Attached Knowledge, and Checklist each get their own section
  3. Visual formatters can share sections — Process Flow, Parent Breadcrumb can be in the same section as form fields (as the first element)

sys_ui_formatter Records

Before adding any formatter (except Activity and Attached Knowledge), verify the sys_ui_formatter record exists using a 3-level cascade query:

  1. Target table: table=<table_name>^formatter=<macro_name>
  2. Global scope: table=global^formatter=<macro_name>
  3. Parent table: table=<parent_table>^formatter=<macro_name> (if table extends another)

If found, use the existing record. If not found, create one:

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

const myFormatter = Record({
$id: Now.ID['my-formatter'],
table: 'sys_ui_formatter',
data: {
name: 'Formatter Display Name',
type: 'formatter',
formatter: '<macro_name>',
table: '<target_table>',
active: true,
// Include angular_module if required (Checklist, CI Relations)
// angular_module: 'module.name',
},
})

Some formatters require an angular_module field:

  • Checklist: 'inlineChecklist'
  • CI Relations: 'sn.ciRelationFormatter'
  • Contextual Search: 'sn.cxs.table_search' (optional — verify on your instance)

Examples

Activity Stream (Global Formatter)

No sys_ui_formatter record needed — use the Formatter constant directly.

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

Form({
table: 'x_myapp_task',
view: default_view,
sections: [
{
caption: 'Details',
content: [
{
layout: 'one-column',
elements: [
{ field: 'name', type: 'table_field' },
{ field: 'description', type: 'table_field' },
],
},
],
},
{
caption: 'Activity',
content: [
{
layout: 'one-column',
elements: [
{ type: 'formatter', formatterRef: Formatter.Activities_Filtered },
],
},
],
},
],
})

Prerequisites: Available by default for every table. No additional setup required.

Process Flow with Stage Records

Requires a sys_ui_formatter record and sys_process_flow stage records as prerequisites.

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

const processFlowFormatter = Record({
$id: Now.ID['process-flow-formatter'],
table: 'sys_ui_formatter',
data: {
name: 'Process Flow',
type: 'formatter',
formatter: 'process_flow.xml',
table: 'x_myapp_task',
active: true,
},
})

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

Record({
$id: Now.ID['stage-active'],
table: 'sys_process_flow',
data: {
active: true,
condition: 'state=active^EQ',
label: 'Active',
name: 'Task Flow - Active',
order: '200',
table: 'x_myapp_task',
},
})

Record({
$id: Now.ID['stage-closed'],
table: 'sys_process_flow',
data: {
active: true,
condition: 'state=closed^EQ',
label: 'Closed',
name: 'Task Flow - Closed',
order: '300',
table: 'x_myapp_task',
},
})

Form({
table: 'x_myapp_task',
view: default_view,
sections: [
{
caption: 'Overview',
content: [
{
layout: 'one-column',
elements: [
{ type: 'formatter', formatterRef: processFlowFormatter },
],
},
{
layout: 'two-column',
leftElements: [
{ field: 'name', type: 'table_field' },
{ field: 'state', type: 'table_field' },
],
rightElements: [
{ field: 'priority', type: 'table_field' },
{ field: 'assigned_to', type: 'table_field' },
],
},
],
},
],
})

Prerequisites: Stage Model must be configured in sys_process_flow for the target table.

Checklist Formatter

Requires the Checklist plugin and a task-derived table.

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

const checklistFormatter = Record({
$id: Now.ID['checklist-formatter'],
table: 'sys_ui_formatter',
data: {
name: 'Checklist Formatter',
type: 'formatter',
formatter: 'inline_checklist_macro.xml',
angular_module: 'inlineChecklist', // REQUIRED for Checklist
table: 'x_myapp_task',
active: true,
},
})

Form({
table: 'x_myapp_task',
view: default_view,
sections: [
{
caption: 'Task Details',
content: [
{
layout: 'one-column',
elements: [
{ field: 'name', type: 'table_field' },
{ field: 'state', type: 'table_field' },
],
},
],
},
{
caption: 'Checklist',
content: [
{
layout: 'one-column',
elements: [
{ type: 'formatter', formatterRef: checklistFormatter },
],
},
],
},
],
})

Prerequisites:

  • Checklist plugin enabled
  • Target table supports checklists (typically task-derived)
  • Checklist definitions/items exist or can be added
  • User has rights to view/edit checklist items

Attached Knowledge Formatter

Display knowledge articles linked to a task record. Uses the global Formatter.Attached_Knowledge constant — no sys_ui_formatter record needed.

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

Form({
table: 'incident',
view: default_view,
sections: [
{
caption: 'Incident Details',
content: [
{
layout: 'one-column',
elements: [
{ field: 'short_description', type: 'table_field' },
{ field: 'description', type: 'table_field' },
],
},
],
},
{
caption: 'Knowledge',
content: [
{
layout: 'one-column',
elements: [
{
type: 'formatter',
formatterRef: Formatter.Attached_Knowledge,
},
],
},
],
},
],
})

Prerequisites:

  • Knowledge Management plugin installed
  • At least one knowledge article is attached to the record
  • Target table is task or extends from task

CI Relations Formatter

Display CI relationship maps on CMDB records. Requires creating a sys_ui_formatter record first.

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

const ciRelationsFormatter = Record({
$id: Now.ID['ci-relations-formatter'],
table: 'sys_ui_formatter',
data: {
formatter: 'ui_ng_relation_formatter.xml',
angular_module: 'sn.ciRelationFormatter', // REQUIRED for CI Relations
name: 'CI Relations',
table: 'cmdb_ci',
type: 'formatter',
active: true,
},
})

Form({
table: 'cmdb_ci_server',
view: default_view,
sections: [
{
caption: 'Server Details',
content: [
{
layout: 'one-column',
elements: [
{
type: 'formatter',
formatterRef: ciRelationsFormatter,
},
{ field: 'name', type: 'table_field' },
{ field: 'ip_address', type: 'table_field' },
],
},
],
},
],
})

Prerequisites:

  • CMDB plugin installed
  • Target table extends cmdb_ci
  • Relationship records exist in cmdb_rel_ci with appropriate relationship types

Contextual Search Formatter

Adds a search results panel to an EXISTING form that displays contextual knowledge articles or catalog items based on field values as the user types. This formatter enhances platform forms (incident, task, custom tables) — it does NOT create a new search page or interface. Requires CXS configuration records.

How CXS Works (Architecture)

Form record (e.g. incident)
└─ field value (e.g. short_description = "VPN not connecting")
└─ used as search term
└─ searches Knowledge Base (kb_knowledge), Catalog, or Communities
└─ matching articles displayed in the formatter panel on the form

Key architectural concepts:

  • cxs_table_config.table — the form table (e.g., incident). This is the table whose field values drive the search.
  • cxs_table_field_config.field — the field on the form table whose value is sent as the search term.
  • cxs_context_config — defines the search sources (KB, Catalog, Communities). These are platform system records — never create them. Query to find available ones.
  • CXS searches kb_knowledge (Knowledge Base), catalog items, and communities. It does NOT search custom tables.

Critical CXS Limitations

  • CXS cannot search custom tables — it only searches Knowledge Base (kb_knowledge), Catalog items, and Communities. Never create a custom knowledge table and attempt to wire it to CXS.
  • Knowledge articles must exist as kb_knowledge records inside an active Knowledge Base. If the user asks for a "contextual panel showing knowledge articles", those articles must be in the platform KB, not a custom table.
  • Query existing search contexts firstcxs_context_config records are platform system records. Query the instance for active records in table cxs_context_config with fields sys_id, name before configuring (see query topic).

CXS Setup Process

Step 1: Get available search contexts

Query the instance for table cxs_context_config for all records with fields sys_id, name, description (see query topic for how to perform queries in your environment).

Common context: Knowledge Base Search. Ask the user to select a search context if multiple are available.

Step 2: Create cxs_table_config record

The table field here is the form table (e.g., incident, sn_si_incident) — not the KB table. The match_condition is an encoded query on this same form table to control when the formatter is active.

CRITICAL: match_condition field validation

The match_condition field must only use fields that exist on the form table. Before using a field in match_condition:

  1. Query the instance for table sys_dictionary where name=<form_table> and element=<field_name>, retrieving field element to verify the field exists (see query topic)
  2. Use sys_created_on!=NULL^EQ as a safe fallback if unsure (always true for saved records)

Step 3: Verify string fields on form table

For cxs_table_field_config, you need to identify string fields available on the form table.

First, query the instance for table sys_glide_object where name=string to get the String type sys_id (see query topic).

Then query the instance table sys_dictionary where name=<form_table> and internal_type=<string_type_id>, retrieving field element to find available string fields (see query topic).

Prefer short_description or description. If neither exists, ask the user to select an appropriate string field.

Full Example

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

// Create CXS table config
const cxsTableConfig = Record({
$id: Now.ID['incident-cxs-config'],
table: 'cxs_table_config',
data: {
table: 'incident', // The form table - drives the search
name: 'Incident[incident]', // Format: "Label[table_name]"
active: true,
cxs_context_config: '<sys_id_from_query>', // From step 1
ui_type: 'platform',
results_header_text: 'Related Search Results',
limit: 10,
results_per_page: 10,
match_condition: 'short_descriptionISNOTEMPTY^category=software', // Encoded query on form table
related_search_active: true,
enable_source_selector: true,
},
})

// Create CXS field config
const cxsFieldConfig = Record({
$id: Now.ID['incident-cxs-field-config'],
table: 'cxs_table_field_config',
data: {
active: true,
cxs_table_config: cxsTableConfig,
field: 'short_description', // Field on form table used as search term
name: 'Short Description[short_description]', // Format: "Field Label[field_name]"
sys_name: 'Short Description[short_description]',
default_config: true,
weight: 100,
},
})

// Create formatter
const cxsFormatter = Record({
$id: Now.ID['incident-cxs-formatter'],
table: 'sys_ui_formatter',
data: {
formatter: 'cxs_table_search.xml',
name: 'Contextual Search',
table: 'incident',
type: 'formatter',
active: true,
// angular_module: 'sn.cxs.table_search', // May be optional - verify on your instance
},
})

Form({
table: 'incident',
view: default_view,
sections: [
{
caption: 'Details',
content: [
{
layout: 'one-column',
elements: [
{ field: 'short_description', type: 'table_field' },
{
type: 'formatter',
formatterRef: cxsFormatter,
},
{ field: 'category', type: 'table_field' },
],
},
],
},
],
})

Prerequisites:

  • Contextual Search enabled and configured on the instance
  • Knowledge articles exist as kb_knowledge records in an active Knowledge Base
  • cxs_table_config must be configured for the form table
  • match_condition uses fields that exist on the table

Parent Breadcrumb Formatter

Show parent breadcrumb navigation on hierarchical task records. Requires a field named exactly parent.

Critical Field Requirement

The table MUST have a reference field named exactly parent.

Field NameAcceptable
parent✅ Yes
parent_task❌ No
parent_ticket❌ No
parent_record❌ No

The field must:

  • Be named exactly parent (case-sensitive)
  • Be a reference field type
  • Reference the same table the record is on, or a table it extends from (e.g., the base task table)

If this field does not exist, create it before adding the formatter.

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

const parentBreadcrumbFormatter = Record({
$id: Now.ID['parent-breadcrumb-formatter'],
table: 'sys_ui_formatter',
data: {
formatter: 'parent_crumbs.xml',
name: 'Parent Breadcrumb',
table: 'incident',
type: 'formatter',
active: true,
},
})

Form({
table: 'incident',
view: default_view,
sections: [
{
caption: 'Incident',
content: [
{
layout: 'one-column',
elements: [
{
type: 'formatter',
formatterRef: parentBreadcrumbFormatter,
},
{ field: 'number', type: 'table_field' },
{ field: 'parent', type: 'table_field' },
],
},
],
},
],
})

Prerequisites: Table must have a field named exactly parent (reference type, not parent_task or parent_record).

Variables Editor Formatter

Display variable/question values from record producers on task-derived tables.

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

const variablesFormatter = Record({
$id: Now.ID['variables-formatter'],
table: 'sys_ui_formatter',
data: {
formatter: 'com_glideapp_questionset_default_question_editor',
name: 'Variables',
table: 'sc_req_item',
type: 'formatter',
active: true,
},
})

Form({
table: 'sc_req_item',
view: default_view,
sections: [
{
caption: 'Request Details',
content: [
{
layout: 'one-column',
elements: [
{ field: 'number', type: 'table_field' },
{ field: 'short_description', type: 'table_field' },
],
},
],
},
{
caption: 'Variables',
content: [
{
layout: 'one-column',
elements: [
{
type: 'formatter',
formatterRef: variablesFormatter,
},
],
},
],
},
],
})

Prerequisites:

  • Target table extends task
  • Record created from a record producer
  • Questions/variables are defined for the table

Position: No fixed position requirement — place where variable questions logically fit in the form flow.

Best Practices

  1. Use Formatter constants: Always prefer Formatter.Activities_Filtered over raw GUID strings for readability and maintainability
  2. Place activity streams in dedicated sections: Activity formatters are best at the bottom of the form in their own section — this matches ServiceNow's standard layout convention and creates a separate tab in the UI
  3. Don't duplicate formatters: Each formatter should appear only once per form — adding the same formatter twice causes rendering issues
  4. Check formatter availability: Before using a formatter, verify the sys_ui_formatter record exists on the target instance using the 3-level cascade query (target table → global → parent table)
  5. Use one-column layouts: Formatters span the full width — always use layout: 'one-column' for formatter elements
  6. Validate prerequisites before adding: Process Flow needs stage records, CXS needs config records, Parent Breadcrumb needs a parent field — verify these exist before adding the formatter element

Avoidance

  • Never create sys_ui_formatter records for Activity or Attached Knowledge — they exist globally on all instances.
  • Never create custom formatter macros — not supported in Fluent. Only use built-in formatters.
  • Never place formatters in two-column layout blocks — formatters need full width. Always use one-column.
  • Never duplicate formatters on the same form — adding the same formatter twice causes rendering issues.
  • Never use the Parent Breadcrumb formatter without a field named exactly parent — must be a reference type (not parent_task, parent_record).
  • Never create cxs_table_config without verifying the match_condition uses fields that exist on the form table — query the instance for table sys_dictionary where name=<form_table> and element=<field_name>, retrieving field element to verify the field exists (see query topic).
  • Never hardcode formatter GUIDs — use Formatter.* constants for Activity and Attached Knowledge, or a Record<'sys_ui_formatter'> reference for all others.
  • Never skip the sys_ui_formatter query — always check if the record exists before creating a new one using the 3-level cascade (target table → global → parent table).
  • Never create a custom knowledge table for CXS — CXS only searches kb_knowledge, catalog items, and communities. Custom tables are not supported as search sources.
  • Never omit angular_module for Checklist or CI Relations — these formatters require the angular_module property in the sys_ui_formatter record or they will fail at runtime.

Next Steps

  • For API properties and full type reference, see the form-api topic
  • For table creation and field management, see the table topic
  • For service catalog and record producer configuration, see the service-catalog topic