Clone the Header Widget

If you are trying to clone the “Header Menu” widget to make modifications, you’ll notice it isn’t so easy. This article takes through the obstacles of cloning that widget. If this worked for you, let’s hear it in the comments!

Clone Header Widget

  1. Login as admin

  2. Left Navigator Bar > Service Portal Menus > Pick your Menu

  3. Open Widget: Header Menu.

  4. Clone Widget

  5. Rename to Header Menu v2

  6. On this line of HTML change the ng-include="'menuTemplate'" to ng-include="'menuTemplateCustom'"

<li ng-repeat="item in visibleItems" ng-class="{dropdown: item.items.length > 0, 'header-menu-item': true}" ng-include="'menuTemplateCustom'" role="presentation"></li>

7. Click Update

Copy Angular Templates

The angular templates don’t copy when you clone a Header Menu widget. You can find angular templates at the bottom of the form.

  1. Open the old Header Widget: Header Menu

  2. Open each Angular Template at the bottom of the form

  3. Rename the ID and Widget and click INSERT AND STAY

  • ID: menuTemplateCustom | Widget: Header Menu v2

  • ID: item-added-tooltip.htmlCustom | Widget: Header Menu v2

  • ID: spDropdownTreeCustom | Widget: Header Menu v2


Under Service Portal > Angular Providers, is where you add a directive.

You need to add a directive as a there is a new template, and sp-dropdown-tree doesn’t handle that yet. I would think they may have this fixed in a future version of ServiceNow, but not that many people are cloning the Header Menu widget.

Directive: spDropdownTreeCustom
function () {
	return {
		restrict: 'E',
		scope: {items: '='},
		replace: true,
		template: '<ul class="dropdown-menu">' +
		'<li ng-repeat="mi in items" style="min-width: 20em;" ng-class="{\'dropdown-submenu\': mi.type == \'menu\', \'dropdown-menu-line\':$index < items.length - 1}" ng-include="getURL()">' +
		link : function(scope, element, attrs, controller) {
			scope.getURL = function() {
				return 'spDropdownTreeCustom';
(function($) {
	$("body").on( "click", "a.menu_trigger", function(e) {
		var current = $(this).next();
		var grandparent = $(this).parent().parent();
		if ($(this).hasClass('left-caret') || $(this).hasClass('right-caret'))
			$(this).toggleClass('right-caret left-caret');
		grandparent.find('.left-caret').not(this).toggleClass('right-caret left-caret');
		$(".dropdown-menu").each(function(i, elem) {
			var elemClosest = $(elem).closest('.dropdown');
			var currentClosest = current.closest('.dropdown');
			if (!elem.contains(current[0]) && elem != current[0] && (!currentClosest.length || !elemClosest.length || elemClosest[0] == currentClosest[0]))
	$("body").on( "click", "a:not(.menu_trigger)", function() {
		var root=$(this).closest('.dropdown');
		root.find('.left-caret').toggleClass('right-caret left-caret');

Add Provider

In your new widget, Header Menu v2, at the bottom of form, add the provider, spDropdownTreeCustom

Modify Templates

In the new templates you created, spDropdownTreeCustom and menuTemplateCustom. Modify lines of code that mention “sp-dropdown-tree” to “sp-dropdown-tree-custom”


<sp-dropdown-tree-custom role="menu" aria-label="{{::item.label}}" ng-if="item.scriptedItems.count > 0" items="item.scriptedItems.items" />


<sp-dropdown-tree-custom ng-if="mi.type == 'menu' && mi.items.length" items="mi.items" />

Add Header Menu v2 to your Menu

Add your new Header Menu v2 widget to the menu of your choice.

If you receive an error on load, maybe missed a step in the instructions above.