Service Portal

Create a Service Portal Widget

Service Portal widgets let you build custom UI components that display data or capture user input without the limitations of stock portal pages. This guide walks you through creating a widget from scratch using the Widget Editor.

Why custom widgets beat stock portal pages

Stock Service Portal pages work for basic needs, but fall short when you need custom data displays, specific user interactions, or UI components that don't exist out of box. Before widgets, portal customization meant either hacking existing pages (fragile) or building entire custom pages (overkill). Widget development affects two groups: platform developers who build them and end users who interact with them through portal pages.

How Service Portal widgets work

A widget has four components: HTML template for structure, CSS for styling, client script (AngularJS 1.x controller) for user interactions, and server script for data queries. The server script runs first and populates a data object that gets passed to the client controller. Most widgets start simple — server script queries a table, client script displays the results. Production widgets add error handling, loading states, user input validation, and spUtil service calls for portal-specific operations like navigation and user context.

From working widget to production-ready component

Once your basic widget works, the improvements that matter are: proper error handling for failed queries, loading indicators while data fetches, input validation before server calls, and mobile-responsive CSS. Advanced widgets integrate with portal theming, handle real-time updates via server-sent events, and use widget dependencies to share code across multiple widgets. The difference between a demo widget and a production one is graceful failure handling and user experience polish.

Before you start

  • sp_admin or admin role
  • Service Portal instance configured and accessible
Sourdough
Chrome Extension

Sourdough: ServiceNow Monitoring and Analytics

A Chrome extension for ServiceNow Admins and Developers with essential tools, analytics, graphs and monitoring features.

Instance HealthGraphs & ChartsAPI HealthDeveloper ToolsQuick SearchInstance Switcher
Add to Chrome

Free to install. Pro $5/month after a 14-day no-card trial.
Pro requires the ServiceNow admin role. Upgrade inside the extension.

Overview
Tasks
CMDB
API
Metrics
Monitor
Internals
Instance:sourdoughdev·Version:Yokohama
Instance StateONLINE
System StatusFully Operational
Session Timeout90 minutes
Logged-In Sessions2 (20 active)
Build Nameyokohama-12-18-2024_p1
IP Address10.159.128.43
Instance HealthHealth Score: 90%
🔥 5dSourdough (Chrome Plugin)Dark Mode

Step by step

1

Open the Widget Editor

Navigate to Service Portal > Widgets and click New. The Widget Editor opens with four tabs: HTML Template, CSS - SCSS, Client Script, and Server Script. Name your widget something descriptive — you'll reference this in portal page configurations later. The ID field auto-populates from the name and can't be changed after save.

TIP

Widget IDs become part of the DOM, so avoid spaces and special characters even though ServiceNow allows them.

2

Write the server script

Click the Server Script tab and write the GlideRecord query or other server-side logic. Populate the data object with whatever the client needs — data.records = results.getTableRecords(), data.userRole = gs.hasRole('admin'), etc. The data object gets serialized and passed to the client controller automatically. Keep queries efficient — this runs every time the widget loads.

TIP

Use data.loading = true initially, then set data.loading = false after queries complete to show loading states on the client.

3

Build the HTML template

Click HTML Template and write AngularJS 1.x template markup. Reference server data with expressions like {{data.records.length}} or ng-repeat="item in data.records". Use Bootstrap classes for styling — Service Portal includes Bootstrap 3 by default. Add ng-click handlers that call client controller functions: ng-click="c.doSomething(item)".

TIP

Wrap your template in a single div with a widget-specific class for CSS scoping.

4

Implement the client controller

Click Client Script to write the AngularJS controller. Access server data via data, call server functions with c.server.get() or c.server.update(), and use spUtil service for portal operations. Common spUtil methods: spUtil.recordWatch for real-time updates, spUtil.getURL for navigation, spUtil.addErrorMessage for user feedback. Controller functions get bound automatically — c.functionName becomes available in the HTML template.

5

Add CSS styling

Click CSS - SCSS and write styles scoped to your widget. ServiceNow compiles SCSS to CSS automatically, so you can use variables and nesting. Scope styles to your widget class to avoid conflicts: .my-widget-class { .some-element { color: blue; } }. Portal pages inherit theme CSS, so your widget styling builds on top of existing styles.

6

Test the widget

Save the widget, then navigate to Service Portal > Pages and either create a test page or edit an existing one. Drag your widget from the widget library onto the page. Click the portal URL to view the live widget. Use browser developer tools to debug AngularJS scope issues — look for the ng-scope elements and inspect $scope.data.

7

Handle errors and edge cases

Add server-side error handling with try/catch blocks and populate data.error with user-friendly messages. On the client, check for data.error before rendering content and display appropriate messages. Handle empty result sets gracefully — show "No results found" instead of broken templates. Add loading indicators while server calls execute using data.loading flags.

TIP

Use c.server.refresh() to re-run the server script without a full page reload when users take actions that change data.

Best practices

  • Always validate user input in both client and server scripts — client validation for UX, server validation for security.

  • Keep server scripts fast by limiting GlideRecord queries and avoiding unnecessary table joins — widgets run on every page load.

  • Use spUtil.recordWatch() instead of polling when you need real-time updates — it's more efficient and provides better user experience.

  • Scope CSS classes to your specific widget to avoid style conflicts when multiple widgets exist on the same page.

  • Handle mobile breakpoints in your CSS — portal users frequently access widgets on phones and tablets.

Test Your Knowledge

Quick 3-question quiz — see how your ServiceNow skills stack up.

Question 1 of 3Performance

A list view on a table with millions of records is slow. Best fix?

Select an answer to continue