clear light bulb placed on chalkboard

Configurable Workspace is a really great ServiceNow product. Even the legacy one, the original Agent Workspace was a big step forward. I remember first seeing this, it was so impressive! I had big hopes which grew bigger when they announced the possibility to built custom components. I tried, built something, it worked, I was happy. Recently I’ve came back to this topic, hoping to see improvements… But I saw the same old stuff. It will be improved, I know. Custom components will use supported version of node.js to develop. We just have to wait.

But this post is about something else. A friend of mine recently asked me how to open a custom page designed for service portal in Service Operations Workspace. He wanted to open it in a tab, next to active record form, from a UI Action. Sounds simple? Should be, shouldn’t it? There must be an API for this, it seems like a valid use case, easy to implement, right?

Teaser – final result

Spoiler alert! 😉


Well, no. Whether you do your research on big Internet search engines or on ServiceNow documentation page, you will find no direct solution. Just some hints. Some sources suggest g_navigate (which doesn’t work in configurable workspace), other suggest using a modal instead of opening a new tab (which actually works and can also be considered, depending on your requirements) with g_modal.show_frame({}) (undocumented, but you can find examples on OOB instance).

There is also an API called g_aw. I had high hopes for it, but:

  • it only contains openRecord() and closeRecord() methods
  • when you try to be smart and open sp_page record, you get exaclty what you ask for – a form, backend view of the page

This wasn’t the best approach. So I had to take a step back and analyze how the other pages are displayed in tabes. Records, sometimes catalog item page (so it is possible to show page from service portal!). I was actually lucky that my friend’s use case was around interactions. On OOB instance, there is a Create Request UI Action, which opens a catalog item page with the portal look & feel.


So how does it work? Let’s dive deeper. The UI Action has a pretty simple script with the most interesting part here:

var params ={};
params.sysparm_parent_table = "interaction";
params.sysparm_parent_sys_id = g_form.getUniqueValue();
g_service_catalog.openCatalogItem('sc_cat_item', '-1', params);

Ah, so there is g_service_catalog API! Unfortunatelly, it has offically only one method, not the one used. When you use it, you will be redirected to the following URL:

I split it on purpose to facilitate analysis. Now, what you see here is:

  • Line 1 is the instance URL
  • Line 2 is the SOW path
  • Line 3 is the page with required parameters
  • Line 4 is interesting. I can’t explain it yet, but it seems to be a divider to allow another tab to be opened
  • Line 5 is same as 3. Notice the sc_cat_item passed as a table required parameter
  • Line 6 provides optional params to the page call from previous line

Next step? Let’s check the record page in UI Builder. OOB it may look like that:

So the sc_cat_item case, used by Create Request UI Action is a page variant with a table=sc_cat_item condition!

We are getting closer and closer – let’s see what they use to display sc_cat_item page:

It’s a custom component. Which gives us more hints:

  • It is not yet possible OOB to open portal pages in workspace environment
  • SN used custom component to achieve that – this means that regular iFrame will cause problems


OK, that was a good brain experience. Now what you need to implement it?

  • A new record page variant in your workspace. This means, you will have to add it for every workspace where you want to use it. No surprise here.
  • A UI Action to open the custom page.

How you implement it is up to you. However, remember about couple of things

  • Since this is a variant, you gave to utilise mandatory parameters table and sysId. I used table for the condition and sysId for the page to be displayed along with any parameters
  • In the iFrame component, make sure to check Disable sandbox. Otherwise you will get tons of errors and nothing else
  • Provide the source of your iFrame component as a script result and utilise the api.context.props.sysId parameter
  • Now you can write just one line in your client UI Action (yes, it has to be marked as Client) – g_service_catalog.openCatalogItem('page', 'the page you want to see with parameters');

Final touches

There are two more things worth noting down. First of all, consider displaying the page on a portal without unnecesary branding. You may even create a new one, with minimal theme and no header/footer, just to display pages from service portal in the workspace.

And secondly, you may have noticed that the new tab displaying the portal page has an ugly title: Loading… And it doesn’t change. Why? Since it’s a UI Builder page, the answer is simple – because you didn’t write any instructions on the page when and how to do that. You may take a look at the OOB Catalog Item Page SNC variant. Check client scripts there and examine this one:

* @param {params} params
* @param {api} params.api
* @param {any} params.event
* @param {any} params.imports
* @param {ApiHelpers} params.helpers
function handler({api, event, helpers, imports}) {
    if (event.payload && event.payload.title)
        api.emit('SCREEN_STATUS_CHANGED', {
            title: event.payload.title
    else if (event.payload)
        helpers.translate("Custom tab title").then(
            (value) => {
                api.emit('SCREEN_STATUS_CHANGED', {
                 "title" : value

You set the tab title on line 14. Now all you have to do is to run this script. How? Via events – iFrame components has one event, iFrame Loaded. Add a script execution there and there you go!


With the UI Builder getting more and more updates a lot is possible. Learn about it, build something on it and understand it. This is the only way to be able to create custom solutions to problems which shouldn’t exist 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *