SharePoint Framework Extensions - Application Customizer Overview

Overview

SharePoint Framework (SPFx) Extension allows extending the SharePoint user experience. Using SharePoint Framework Extensions, we can customize the overall SharePoint user experience involving notification area, list views, and toolbars. Read more about SPFx extensions here.

In this article, we will explore the application customizer part of SharePoint extensions.

Brief information about Application Customizer

Application customizer provides access to predefined locations on the SharePoint page and allows us to customize them. In this article, we will add header and footer to the SharePoint site using application customizer.

In the classic SharePoint, we used to extend predefined HTML elements or placeholders from master page to display the custom content. In the modern SharePoint, as JavaScript cannot be used on a page, the application customizer helps to implement these scenarios.

 
Page Placeholders

The application customizer helps to get access to the below zones on a Modern SharePoint page
  • Top: Header section of the page
  • Bottom: Footer section of the page
Create SPFx Solution

Open the command prompt. Create a directory for an SPFx solution.
  1. md spfx-extensions-applicationcustomizer  
Navigate to the above created directory.
  1. cd spfx-extensions-applicationcustomizer  
Run Yeoman SharePoint Generator to create the solution.
  1. yo @microsoft/sharepoint  
Yeoman generator will present you with the wizard by asking questions about the solution to be created.

Yeoman generator for SPFx 
 
Solution Name

Hit enter to have a default name (spfx-logging in this case) or type in any other name for your solution.
Selected choice - Hit enter
 
Target for component

Here we can select the target environment where we are planning to deploy the client webpart; i.e. SharePoint Online or SharePoint OnPremise (SharePoint 2016 onwards).
Selected choice - SharePoint Online only (latest)
 
Place of files

We may choose to use the same folder or create a subfolder for our solution.
Selected choice - Same folder
 
Deployment option

Selecting Y will allow the app to deploy instantly to all sites and will be accessible everywhere.
Selected choice - N (install on each site explicitly)
 
Type of client-side component to create

We can choose to create client side webpart or an extension.
Selected choice - Extension
 
Type of client-side extension to create

We can choose to create Application customizer, Field customizer, or ListView Command Set.
Selected choice - Application customizer
 
Application customizer name

Hit enter to select the default name or type in any other name.
Selected choice - CustomHeaderFooter
 
Application customizer description

Hit enter to select the default description or type in any other value.
Selected choice - Adds custom header and footer to the SharePoint site
 
Yeoman generator will perform a scaffolding process to generate the solution. The scaffolding process will take a significant amount of time.

Once the scaffolding process is completed, lock down the version of project dependencies by running the below command
  1. npm shrinkwrap  
In the command prompt type the below command to open the solution in the code editor of your choice.
  1. code .  
Solution Structure

The solution structure is similar to client-side web parts with similar configuration options.

SharePoint Extension Solution Structure

 
The file CustomHeaderFooterApplicationCustomizer.manifest.json inside the folder “\src\extensions\customHeaderFooter\” defines the extension type and unique identifier for the solution. Please note down the id. We will need it later for debugging purposes. 
  1. {  
  2.   "$schema""https://developer.microsoft.com/json-schemas/spfx/client-side-extension-manifest.schema.json",  
  3.   
  4.   "id""f58a0902-c9d8-4cce-bd5e-4da887e21bed",  
  5.   "alias""CustomHeaderFooterApplicationCustomizer",  
  6.   "componentType""Extension",  
  7.   "extensionType""ApplicationCustomizer",  
  8.   
  9.   // The "*" signifies that the version should be taken from the package.json  
  10.   "version""*",  
  11.   "manifestVersion": 2,  
  12.   
  13.   // If true, the component can only be installed on sites where Custom Script is allowed.  
  14.   // Components that allow authors to embed arbitrary script code should set this to true.  
  15.   // https://support.office.com/en-us/article/Turn-scripting-capabilities-on-or-off-1f2c515f-5d7e-448a-9fd7-835da935584f  
  16.   "requiresCustomScript"false  
  17. }  
Implement Application Customizer

Open CustomHeaderFooterApplicationCustomizer.ts under “\src\extensions\customHeaderFooter\” folder.

To get access to placeholders on the page, use the below imports
  1. import {  
  2.   BaseApplicationCustomizer,  
  3.   PlaceholderContent,  
  4.   PlaceholderName  
  5. } from '@microsoft/sp-application-base';  
Update the interface IAlertApplicationCustomizerProperties to include Top and Bottom properties
  1. export interface ICustomHeaderFooterApplicationCustomizerProperties {  
  2.   Top: string;  
  3.   Bottom: string;  
  4. }  
Implement OnInit method
  1. public onInit(): Promise<void> {  
  2.     Log.info(LOG_SOURCE, `Initialized ${strings.Title}`);  
  3.   
  4.     // Added to handle possible changes on the existence of placeholders.  
  5.     this.context.placeholderProvider.changedEvent.add(thisthis._renderPlaceHolders);  
  6.       
  7.     // Call render method for generating the HTML elements.  
  8.     this._renderPlaceHolders();  
  9.   
  10.     return Promise.resolve();  
  11. }  
Implement _renderPlaceHolders method 
  1. private _renderPlaceHolders(): void {  
  2.     console.log('HelloWorldApplicationCustomizer._renderPlaceHolders()');  
  3.     console.log('Available placeholders: ',  
  4.     this.context.placeholderProvider.placeholderNames.map(name => PlaceholderName[name]).join(', '));  
  5.       
  6.     // Handling the top placeholder  
  7.     if (!this._topPlaceholder) {  
  8.       this._topPlaceholder =  
  9.         this.context.placeholderProvider.tryCreateContent(  
  10.           PlaceholderName.Top,  
  11.           { onDispose: this._onDispose });  
  12.       
  13.       // The extension should not assume that the expected placeholder is available.  
  14.       if (!this._topPlaceholder) {  
  15.         console.error('The expected placeholder (Top) was not found.');  
  16.         return;  
  17.       }  
  18.       
  19.       if (this.properties) {  
  20.         let topString: string = this.properties.Top;  
  21.         if (!topString) {  
  22.           topString = '(Top property was not defined.)';  
  23.         }  
  24.       
  25.         if (this._topPlaceholder.domElement) {  
  26.           this._topPlaceholder.domElement.innerHTML = `  
  27.             <div class="${styles.app}">  
  28.               <div class="ms-bgColor-themeDark ms-fontColor-white ${styles.top}">  
  29.                 <i class="ms-Icon ms-Icon--Info" aria-hidden="true"></i> ${escape(topString)}  
  30.               </div>  
  31.             </div>`;  
  32.         }  
  33.       }  
  34.     }  
  35.       
  36.     // Handling the bottom placeholder  
  37.     if (!this._bottomPlaceholder) {  
  38.       this._bottomPlaceholder =  
  39.         this.context.placeholderProvider.tryCreateContent(  
  40.           PlaceholderName.Bottom,  
  41.           { onDispose: this._onDispose });  
  42.       
  43.       // The extension should not assume that the expected placeholder is available.  
  44.       if (!this._bottomPlaceholder) {  
  45.         console.error('The expected placeholder (Bottom) was not found.');  
  46.         return;  
  47.       }  
  48.       
  49.       if (this.properties) {  
  50.         let bottomString: string = this.properties.Bottom;  
  51.         if (!bottomString) {  
  52.           bottomString = '(Bottom property was not defined.)';  
  53.         }  
  54.       
  55.         if (this._bottomPlaceholder.domElement) {  
  56.           this._bottomPlaceholder.domElement.innerHTML = `  
  57.             <div class="${styles.app}">  
  58.               <div class="ms-bgColor-themeDark ms-fontColor-white ${styles.bottom}">  
  59.                 <i class="ms-Icon ms-Icon--Info" aria-hidden="true"></i> ${escape(bottomString)}  
  60.               </div>  
  61.             </div>`;  
  62.         }  
  63.       }  
  64.     }  
  65.  }  
Use this.context.placeholderProvider.tryCreateContent to get access the placeholder, without assuming that the placeholder will exist
 
Custom Styles

Add a file CustomHeaderFooterApplicationCustomizer.module.scss under “\src\extensions\customHeaderFooter\” folder
  1. .app {  
  2.     .top {  
  3.       height:60px;  
  4.       text-align:center;  
  5.       line-height:2.5;  
  6.       font-weight:bold;  
  7.       display: flex;  
  8.       align-items: center;  
  9.       justify-contentcenter;  
  10.     }  
  11.     
  12.     .bottom {  
  13.       height:40px;  
  14.       text-align:center;  
  15.       line-height:2.5;  
  16.       font-weight:bold;  
  17.       display: flex;  
  18.       align-items: center;  
  19.       justify-contentcenter;  
  20.     }  
  21.   }  
Include the styles in extension CustomHeaderFooterApplicationCustomizer.ts
  1. import styles from './CustomHeaderFooterApplicationCustomizer.module.scss';   
Test the extension

Open serve.json under config folder.

Update the properties section to include Top and Bottom messages.
  1. {  
  2.   "$schema""https://developer.microsoft.com/json-schemas/core-build/serve.schema.json",  
  3.   "port": 4321,  
  4.   "https"true,  
  5.   "serveConfigurations": {  
  6.     "default": {  
  7.       "pageUrl""https://contoso.sharepoint.com/sites/mySite/SitePages/myPage.aspx",  
  8.       "customActions": {  
  9.         "f58a0902-c9d8-4cce-bd5e-4da887e21bed": {  
  10.           "location""ClientSideExtension.ApplicationCustomizer",  
  11.           "properties": {  
  12.             "Top""Header of the page",  
  13.             "Bottom""Footer of the page"  
  14.           }  
  15.         }  
  16.       }  
  17.     },  
  18.     "customHeaderFooter": {  
  19.       "pageUrl""https://contoso.sharepoint.com/sites/mySite/SitePages/myPage.aspx",  
  20.       "customActions": {  
  21.         "f58a0902-c9d8-4cce-bd5e-4da887e21bed": {  
  22.           "location""ClientSideExtension.ApplicationCustomizer",  
  23.           "properties": {  
  24.             "Top""Header of the page",  
  25.             "Bottom""Footer of the page"  
  26.           }  
  27.         }  
  28.       }  
  29.     }  
  30.   }  
  31. }  
In the command prompt, type the below command
  1. gulp serve  
The SharePoint site will open, click “Load debug scripts”

 

Observe the header and footer on the page.

 
 
Summary

Application customizer SharePoint Framework extension helps to extend the predefined placeholders on Modern SharePoint page. These extensions can be deployed tenant wide.