In Service Portal, it’s common to create widgets that are used in multiple places. But if you need change the HTML, developers often clone the widget — a pattern that quickly becomes hard to maintain and results in lots of additional widgets.
Instead, let’s walk through how to build a template-driven, reusable widget that allows you to select and load different HTML templates from the instance options, without cloning the widget or touching the core widget code.
In this example, we’ll look at how to create a new Button widget, similar to the OOB Icon Link widget, but with multiple HTML templates.
Step 1: Add a Reference Field to the Instance Table
Create a reference field on the sp_instance table to allow widget instances to reference an HTML template.
- Navigate to: Service Portal > Widget Instances
- Add a new field:
Column label: Template
Name: u_template
Type: Reference
Reference: Angular ng-template
Use reference qualifier: Advanced
Reference qual:1javascript: "sp_widget=" + current.getValue("sp_widget");Note: to add the advanced reference qualifier, you’ll need to click the “Advanced” related link. - Add the new Template field to the “SP Instance Config” View and “Presentation” Section for any of the Instance tables where you plan on using the templates, e.g. sp_instance_link.

Step 2: Create the Widget
Now, create the widget that will dynamically load templates using the following:
Body HTML template
1 2 3 4 5 6 7 8 9 10 | <div class="button-container"> <div ng-if="options.template" ng-include="::options.template"></div> <div ng-if="!options.template"> <a ng-href="{{::data.href}}" class="block text-center panel panel-{{::options.color}} wrapper-lg {{::options.class_name}}" target="{{::data.target}}"> <div class="fa fa-{{::options.glyph}} fa-3x"></div> <h2>{{::options.title}}</h2> <span class="text-muted">{{::options.short_description}}</span> </a> </div> </div> |
Server Script
1 2 3 4 5 6 7 8 9 10 11 12 13 | (function() { // Template options.template = options.u_template_dv; // Get the link var gr = $sp.getInstanceRecord(); if (gr) { data.href = $sp.getMenuHREF(gr); data.target = options.target || ""; } })(); |
Data table & Fields

Step 3: Add Templates
In the form view of the new widget, start adding templates to the “Angular ng-templates” related list. You can use the following as an example:
Name: template01.html
Template:
1 2 3 4 5 6 7 | <a ng-href="{{::data.href}}" class="block panel panel-{{::options.color}} {{::options.class_name}} text-white" target="{{::data.target}}"> <div class="panel-heading text-center"> <div class="text-center fa fa-{{::options.glyph}} fa-4x"></div> <h2 class="m-t">{{::options.title}}</h2> <div>{{::options.short_description}}</div> </div> </a> |
Step 3: Use the Widget
Now all you have to do is to test it out. Open the designer and add the widget multiple times to the page and try changing the template using the Instance Options context menu.

Next steps
For an even more powerful button widget, you can add an Image field to the Instance table, so that your buttons can incorporate images instead of icons.
If you found this tutorial useful, please let me know in the comments below because I have a lot of techniques like this that I’d like to share.



Founder & CEO of
Interesting, thank you. Please share more.
Hi – Zurich – Fresh PDI:
Done step by step, getting error:
Errorjava.lang.NullPointerException: Cannot invoke “String.hashCode()” because “” is null
Strange. Send me an email with a screen capture of the widget form and I’ll take a look.