Custom modern page header and footer using SharePoint Framework

This post is part 1 of what is currently a 3 part series of updates to my SharePoint Framework application customizer as the capability has evolved since entering developer preview in June. Please see the links to parts 2 and 3 below.

Back in 2015, I published a post on this blog detailing the steps for implementing a custom site header and footer using a SharePoint-hosted add-in. The add-in contained:

1. An add-in part that provides an interface to configure the text and colors associated with the custom header and footer, storing the necessary values in the property bag of the site.

2. Custom JavaScript that is embedded on every (classic) page through the use of user custom actions that reads the values set by the add-in part from the property bag and renders the header and footer on each page.

An add-in part is used to display and update the site header and footer configuration information, which is stored in the site property bag. User custom actions are then used to embed the JavaScript that reads this information and renders the header and footer on all (classic) pages within a site.

Issues with current add-in solution

While the add-in solution from that blog post worked great in SharePoint Online in 2015 (and still works great for SharePoint Online classic pages and SharePoint 2013/2016 on-premises today), SharePoint Online has evolved somewhat over the past two years.

Since late 2016, Microsoft has been rolling out new modern page experiences across SharePoint Online. These include new modern team site pages as well as list and library view pages. The technique I used for embedding custom JavaScript on every page–namely, user custom actions–are not supported on modern pages. Therefore, even with the add-in deployed and configured you will not see a header or footer on any modern page. So how are we supposed to customize them?

Enter SharePoint Framework

The SharePoint Framework gives us a new way to deploy JavaScript-based customizations to SharePoint. These include web parts (for both classic and modern pages) and now Extensions such as Application Customizers that allow you to embed JavaScript on modern pages.

To extend the functionality of my SharePoint-hosted add-in to modern pages using the SharePoint Framework, I created a new SharePoint client-side solution containing an application customizer extension to handle the rendering of the header and footer on all modern pages, reading the same configuration values from the site property bag that are set by the add-in part from my original SharePoint-hosted add-in.

Modern pages contain useful placeholders

When creating the original add-in, I had to do some manipulation of the DOM to insert the <div> elements representing the custom header and footer in the appropriate places on the page using jQuery’s .insertBefore() and .insertAfter() methods. These kinds of customizations are fragile and could break if Microsoft changed the IDs or locations of existing page elements I was referencing such as suiteBarTop or s4-bodycontainer. I was hoping to get away from this in my modern page solution.

Unfortunately, in my initial experiments with page placeholders, I identified some shortcomings that would prevent me from building a complete modern page header/footer solution that leverages them. Most notably, modern site pages do not contain the PageFooter placeholder at all. Also, since my current solution renders the header at the very top of the page, I wasn’t a fan of the PageHeader placeholder location beneath the #suiteBarTop <div> instead of above it at the very top of the page.

SharePoint Framework page placeholders render the PageHeader below the suiteBarTop <div> and do not render a PageFooter on modern site pages, as shown above.

Approach

NOTE: This approach still relies on DOM manipulation and is a fragile and unsupported way to make user interface customizations to modern pages because of dependencies on page elements with specific IDs and CSS classes. In your solutions, please make every effort to leverage the supported page placeholders if possible.

With these limitations in mind and for the sake of a consistent look and feel across all pages (both classic and modern), I decided to stick with my jQuery-based approach of using .insertBefore() and .insertAfter() to inject my custom header and footer at the top and bottom of the page. While investigating the DOM on modern pages to determine where I would need to inject my header and footer, I realized that not all modern pages are created alike. Perhaps this is part of the reason why modern site pages do not contain a PageFooter placeholder…

The <div> element representing the page content on modern list and library view pages (and the modern Site Contents page) has an ID of spoAppComponent. This <div> does not exist on modern site pages. On modern site pages, the <div> element representing the page content has a class of SPPageChrome. (NOTE: modern list and library view pages also contain a <div> with a class of SPPageChrome, but this <div> does not represent the main content area on those pages.)

Page content areas on a modern list/library view page (left) and a modern site page (right) with the custom header and footer injected around them.

In my code, you will see that I test for the existence of #spoAppComponent on the page. If it exists, I know I’m on a modern list or library view page and I inject the custom header and footer around that <div>. Otherwise, I add the header and footer around the <div> with class SPPageChrome.

Building the Application Customizer

I followed the step-by-step instructions from the four-part Office Dev Center article series Build your first extension to generate, build, and deploy my application customizer. The following notes may be useful to you:

– If you have already installed a previous version of Microsoft’s Yeoman SharePoint Client-side Solution Generator, you will need to update it by running npm update -g @microsoft/generator-sharepoint. The current version of the generator (as of the publishing of this post) is now 1.1.1.

– If you haven’t updated your SharePoint Framework development environment in awhile, you may want to run npm update -g to ensure the rest of your global packages are up-to-date as well.

– I added jQuery (npm install –save jquery) and sp-pnp-js (npm install –save sp-pnp-js) to my project and used the PnP JavaScript Core library to simplify the API query for the header and footer configuration values from the site property bag.

– Since I didn’t make use of any ClientSideComponentProperties, I removed that attribute from my ./sharepoint/assets/elements.xml file.

– If you download this code, you will need to update the CDN URL in the ./config/write-manifests.json file to match the location where you deploy the assets from the ./temp/deploy folder in your environment. More information about working with the Office 365 CDN may be found here.

– Remember that you must upload the .sppkg file from the ./sharepoint/solution folder to your tenant’s app catalog site, then add the associated app on your site. This will provision and activate the feature that deploys the application customizer to your site.

I encourage you to download it, try it out in your environment, and let me know if you run into any issues with it. Please keep the following things in mind:

– You must also install the SharePoint-hosted add-in located here in order to configure the header and footer property bag values and render them on classic pages.

– Although the above dependency on my SharePoint-hosted add-in currently exists, it should be possible to move all of these capabilities into a SharePoint Framework solution (i.e., by building a client-side web part to read and write the property bag configuration values and apply the user custom actions to classic pages within the site using the REST API, JSOM, or the PnP JavaScript Core library).

Stay tuned!

This is a very exciting time to be doing modern SharePoint development. SharePoint Framework extensions reached GA in September 2017 and even more new capabilities and options for modern SharePoint customizations are still in the pipeline. Keep an eye on the SharePoint Framework wiki for more announcements and updates!