Spikes Apps

Blog of the Spikes app developers

AngularJS, Azure, Office 365, SharePoint

Optimize performance of your SharePoint Online portal

When customers want an intranet portal, they sometimes insist upon a landing page with a lot of content. Additionally, the page must be very attractive and the content needs to be targeted based upon user profile properties to ensure that the users are presented with the information that is most valuable for them.

This results in a lot of requests to build the page. If your customer has offices all around the world, this is even more difficult because the network performance can be very different.

In this post, I’ll give a overview of the performance optimizations to the solutions that we implemented for our customers to reduce the number and the size of requests to build the page.

Performance enhancements

The Microsoft support article Introduction to performance tuning for SharePoint Online offers a good starting point. Some of the tips below are coming from that article. Other tips are coming from our own experience or from the code reviews we got from Microsoft.


Be careful when using jQuery.getScript to load scripts dynamically. This function adds a timestamped query parameter to each request to enforce the reload of the script:


Because of this query parameter, the browser cache will never be used, not even if you would load the same script multiple times on the same page.

If you want to cache the script, you have to set the cache property globally using $.ajaxSetup() or create your own method to load the scipts using $.ajax().

More information can be found in the jQuery documentation.

Another alternative could be using a loading framework such as RequireJS.


If you want to make cross domain calls in SharePoint, you can use SP.RequestExecutor. However, don’t use SP.RequestExecutor for every call because this will always result in an extra /_api/contextinfo request before the postQuery request.

if (onHostWeb) {
  window.SpikesHelper.getCachedScript(scriptBase + "SP.RequestExecutor.js", 
      function () {
    var executor = new SP.RequestExecutor(window.DataProvider.HostWebUrl());
      url: searchCallUrl,
      method: "POST",
      headers: {
        "accept": "application/json;odata=nometadata",
        "content-type": "application/json; odata=verbose",
        "X-RequestDigest": $('#__REQUESTDIGEST').val()
      success: onSearchSuccessRest,
      error: onSearchFailRest
else {
    'url': searchCallUrl,
    'type': "POST",
    'headers': {
      "accept": "application/json;odata=nometadata",
      "content-type": "application/json; odata=verbose",
      "X-RequestDigest": $('#__REQUESTDIGEST').val()
    'success': function (data) {
    'error': onSearchFailRest

Local storage

Use your browser’s local storage to cache information that doesn’t change frequently such as user profile properties or managed metadata.

Since it may sometimes be necessary to clear this local storage, I suggest providing a button to do this.

saveToStorage: function (key, data, expirationSeconds) {
  "use strict";
  var expirationMS = expirationSeconds * 1000,
    record = { value: data, timestamp: new Date().getTime() + expirationMS };
  localStorage.setItem(key, JSON.stringify(record));
  return data;
loadFromStorage: function (key) {
  "use strict";
  var record = JSON.parse(localStorage.getItem(key));
  if (!record) {
    return null;
  if (new Date().getTime() < record.timestamp) {      return record.value;   }   // The entry has expired, so remove from local storage   localStorage.removeItem(key);   return null; }, clearStorage: function () {   for (var i = localStorage.length - 1; i >= 0; i--) {
    if (localStorage.key(i).startsWith('Spikes')) {

REST batching

Where possible, execute REST commands in batches. You can for instance use Steve Curran’s REST batch executor.

JSON light

You can significantly reduce the payload of your REST request with JSON Light as stated in this blog post.

If you have existing code that uses odata=verbose, be aware that you also have to change the parsing of the results because the format of the returned JSON is different.

In some cases, for instance if you want the edit link of an item, odata=nometadata doesn’t provide enough information and we had to use odata=minimalmetadata.

Search queries

If you need to implement targeting, where possible use search queries. It will take some time before new items become visible on the page, but the request for the data will be quicker since the search results are indexed.

If you have complex queries you might need to do a POST request instead of a GET request.

You can test the performance of your search queries using the SharePoint Search Query Tool.


Use an Azure CDN to host your own custom files. Not only scripts and CSS, but also the images for the branding. The content that is accessed from a CDN is much faster available than the content that is hosted in SharePoint Online.

For some types of files, such as fonts and html templates, you might need to set CORS headers because using a CDN will result in cross domain calls.

For files coming from Azure CDN, you can control the cache behaviour using the cache-control header depending on your environment. If you have clients with a very slow network, You might even consider to set the max-age to the maximum duration (max-age=31536000) and implement cache busting functionality, for instance using a loading framework such as RequireJS.

External script components can also be referenced from a CDN. Since a browser can only handle a number of request from the same host simultaneously, you could even reference external scripts from multiple CDN’s to improve the page load.

Bundling and minification

If you have multiple JavaScript and CSS files, you can bundle and minify those to reduce the number of requests as well as the size of the requests.

Image sprites

If you have a lot of small images in the branding, you can combine those into one image sprite.

Image renditions

Image renditions are slow in SharePoint because the rely upon the cache on the server. Since SharePoint Online is a huge farm with a lot of different servers, your request will probably be served by another server and it is not likely that you will get a cache hit.

SharePoint navigation

As stated in the support article Navigation options for SharePoint Online, you should never use structural navigation. Use search driven navigation or managed navigation instead.

If you are not using Quick Launch, it is best to disable this in Site Settings – Navigation Elements.

Asset library vs Style library

If you have to store files in SharePoint (for instance proprietary files that you don’t want to store on the CDN), consider using the asset library instead of the style library because the style library requires an extra authentication step.

Custom master page

A custom master page is not recommended, but should you still need it, consider adding an extra placeholder at the end of the body section to load your custom scripts.

Home page layout

Most of the time, we started our home page layout with web part zones and we add web parts to present the different components to the user. This provides a lot of flexibility when we want to adapt the home page.

If there are a lot of web part zones and web parts on the page however, it takes a long time for the server to build up that page. The server returns an SPRequestDurarion header that contains the server processing time in millisecond. For a publishing page with 8-10 web parts we observed times of 2-3 seconds and sometimes even more.

We implemented a home page layout based upon AngularJS and now the server processing time is down to 1 second and even less.

If still better performance would be necessary, the next step would perhaps be to build the home page in an Azure App web using the Office UI Fabric and linking from there to the SharePoint Online site.


When designing a landing page for an intranet portal, there are definitely a lot of things you can do to reduce the network traffic and server load.

Tom De Groote

Tom De Groote

Senior SharePoint Architect @ Spikes


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s

%d bloggers like this: