Next Gen Image Compression in Sitecore

Spoiler: This post is not a post about Dianoga, I take a deep dive into Tiny PNG and Kraken.IO integrations into Sitecore. The results are worth checking out at the bottom.


At the start of the year, I’ve picked up where I left off, on page speed. Last year I took a deep dive into attempting to improve the page speed on Sitecore SXA sites by using some of Google’s recommended techniques to structure the page. If you haven’t already seen it, head on over the Sitecore Speedy and see some of the results we achieved.

I’ll be the first to admit that getting really good page speed scores isn’t easy. It takes a lot of different factors to come together. Just as a reminder, here is the main list that I would consider you need to check off to be winning at this game.

1) Introduce image lazy loading

2) Ensure a cache strategy is in place and verify its working.

3) Dianoga is your friend for image compression

4) Use responsive images (must serve up smaller images sizes for mobile)

5) Introduce Critical CSS and deferred CSS files

6) Javascript is not a page speed friend. Defer Defer Defer

For this post, i’m going to look at an alternative to Dianoga. I’m a big fan of Dianoga and have used it over the years to crunch loads of oversized images introduced by Content Editors. I will, however, say that it can add complexity to deployments and CI/CD pipelines and while some claim to have had success in Azure Apps, others have not.

On the flip side, content editors love Tiny PNG, which is one of the most popular image compression website utilities going around. Tiny PNG also has a developer API, so we have used this to build in a compression tool that can be used directly from your Sitecore toolbar.

The button below is hooked up to chat to Tiny PNG API. It will send across your image data and receive a compressed image back for storage.


Full disclosure, I’m not the first person to hook up Tiny PNG to the image library. I could find two other implementations

One will allow you to run a powershell script to connect to the Tiny PNG API and the other is a module to connect to the API on upload.


This implementation of the Tiny PNG API introduces the following variances:

  • A button in the CMS to crunch any single image.
  • A scheduled task that will process any image not already processed.
  • Error handling for when the API limits are reached
  • Logging that outlines which images were processed.
  • Before and After compression information stored in any Image field of choice.
  • A feature toggle to turn the whole feature on/off

All the source code is available at: https://github.com/Aceik/ImageCompression

Now let’s jump in have a look at the results just from crunching a few images down:

Without image compression:

Click to Enlarge Image

To compress the images on the page, we head on over to the “Compress” button in the Media tab that we have introduced.

Click to enlarge

A few examples of compression results taken from homepage images:

Before: 158.4 KB | After: 110.6 KB

Before: 197.8 KB | After: 135.3 KB

Before: 640.0 KB | After: 120.7 KB

After compressing all the images on the page the saving can be seen below.

Click to enlarge

So our total image size saving is 2.4MB – 1.3MB = 1.1MB

A pretty decent saving from just pressing the compress button on 27 homepage images. Also, consider that the user won’t notice any difference in image quality as this method uses lossless compression.


The compression achieved is great for helping us tick off one of the requirements for fast pages with Google. But as we are about to find out Google will likely still complain about two other criteria. When it comes to Google Page Speed insights a page that does not have properly processed images will bring up the following three recommendations:

Here is a break down of how we address each one:

  1. Serve image in next-gen formats – Image formats like JPEG 2000, JPEG XR, and WebP often provide better compression than PNG or JPEG, which means faster downloads and less data consumption. Learn more.
  2. Properly Size images – Your CSS layouts should be responsive and use modern image retrieval techniques that adapt the image size requested based on screen size. Read More
  3. Efficiently encode images – The Tiny PNG integration above will take care of this. This is all about compressing the image to as small as it can get without a visible loss of quality.

So assuming you have already achieved number three using the Tiny PNG integration or another source, let us look at how we can solve the next-gen image requirement.

As a quick side note the testing I did after converting the images to next-gen also ticked item number two above. I don't think this should be relied on however and its best to incorporate responsive images into your projects from the beginning.  

When looking into how to convert images to a next-gen format I opted to target webp. Google has a nice little page explaining the format here.

WebP is natively supported in Google Chrome, Firefox, Edge, the Opera browser, and by many other tools and software libraries.

Once again I opted to look for an API that would provide the conversion for me so that Sitecore could easily connect, send the image and then store the result. All without any extra hosting requirements. I opted to go with Kraken.IO image APIs as they have a free 100MB trial offer and well free is a good price when building proof of concepts. The integration is all available on Aceik’s github repository. Just signup for your own API keys add them to the module settings (in the CMS) and start converting.

To test out just how much this would impact the image payload size for the whole page, I once again converted all the images on the SXA habitat homepage.

Here are the results:

Click to enlarge

So our total image size saving is now 2.4MB – 0.79MB = 1.61MB

The reduction in size from a non-compressed image to a webp formatted image is truly impressive.

A few examples:


Conclusion

I can only conclude by saying that if page speed is really an important factor for your Sitecore project take a look at Tiny PNG. If you want to go next level with your image formats and achieve great compression try out the Kraken.IO API integration as it could be well worth the small subscription fee.


Results

CompressionTotal Image SizeSaving
None2.4 MB
Tiny PNG1.3 MB1.1MB
Kraken.IO (webp)0.79MB1.61MB

Notes:

The module and code mentioned in this blog post are available on Aceik’s Github account. This also contains installations instructions.

GitHub: https://github.com/Aceik/ImageCompression

After installation, your content editors will simply be able to compress and convert images as needed from within the CMS.

Click to enlarge

The Github Readme contains a run down and the standard settings inside Sitecore as shown below:

Sitecore Page Speed: Part 1 : Above The Fold Content

In this series of blogs, I am going to run through ways in which you can increase your score on the Google Page Speed Insights tool.

If you’re not familiar with PageSpeed Insights head over to the page hosted by Google and enter the URL of a Sitecore project you have recently worked on.  It will give you a rank out of 100 and then advise on what is wrong with the way your page HTML, JavaScript and CSS assets are loaded. It will also provide feedback on the way your server is setup and how assets are cached.

If you’re getting a score below 50 on Desktop or Mobile I would suggest it’s time to look at ways you can improve your layouts and renderings.  A score above 80 and you’re really doing pretty well.

If you got a score of 100 … you should probably be writing this blog instead of me.  🙂

Topic 1: Above the fold content / Critical CSS

One of Google’s recommendations is to use a technique called:

  • Above the Fold
  • Critical CSS

As you can imagine this refers to the CSS required to render only the visible part of the page.

In order to do this, you render the critical CSS inline within the head tag. The rest of your CSS is then loaded using a deferred script block approach.  This is all demonstrated in a simple example on this page.

The way this works is the inline minified CSS is delivered over the network as part of the page payload. It is not an external asset and will not block the page load via another network request. The page can, therefore, display the visible section (above the fold) immediately after the page is downloaded.

The bulk of CSS required by the page can be loaded a few moments later via deferred network requests.

What is the best way to construct the minimised block of Inline CSS you ask?

Lucky for us a very handy node tool called “critical” is available for download.

You can really easily spin up a gulp script that will generate all the critical CSS for the main pages across your Sitecore website.

A full example of a gulp critical script can be found here.  Download it and simply run:

  • npm install
  • gulp

This will generate a series of CSS files that contain critical viewport CSS.


In the next blog post, we will cover off how to integrate this inline CSS (above the fold) into our Sitecore layouts.


References:

 

Automate friendly field descriptions

58845601

As you may well know when creating and naming fields in Sitecore we also need to create a friendly title for the content editors. By default Sitecore doesn’t make this as easy as it could be and therefore there are a number of naming conventions and solutions for this simple but fundamental issue.

I have always taken the approach of camel casing my field names. It makes sense from a coding perspective. It is a good practice as most developers understand this convention and its benefits. Also when integrating with ORMs such as glass a camel case field name will map directly to the property name of the class without additional mapping attributes.

The obvious issue with using camel case field names is that the default value the content editors see is the field name. Camel case is great for developers but not as good for content editors, so we need to manually write a friendly display description for the editors. This leads to double entry of field name and field description and then keeping this consistent.

Solution:

Auto generate a friendly field description based on the field name by splitting the camel case and adding spaces. For additional information sometimes required we use the help text option.

Example:

Field Name:

PageTitle

Generated Field Description:

PageTitle1

Addition information:

PageTitle2

Implementation:

Tap into the item on saving event and add some basic code to auto generate a display title based on the field name.

namespace Aceik.Framework.SC.Extensions.Events
{
    using System;
    using System.Text.RegularExpressions;

    using Sitecore.Data;
    using Sitecore.Data.Items;
    using Sitecore.Events;

    /// <summary>The item on saving events.</summary>
    public class ItemOnSavingEvents
    {
        /// <summary>The field title name.</summary>
        private const string FieldNameTitle = "Title";

        /// <summary>The field template id.</summary>
        private readonly ID fieldTemplateId = new ID("{455A3E98-A627-4B40-8035-E683A0331AC7}");

        /// <summary>The on item save.</summary>
        /// <param name="sender">The sender.</param>
        /// <param name="args">The args.</param>
        public void OnItemSaving(object sender, EventArgs args)
        {
            var contextItem = Event.ExtractParameter(args, 0) as Item;

            if (contextItem == null)
            {
                return;
            }

            if (contextItem.TemplateID == this.fieldTemplateId && contextItem.Fields[FieldNameTitle] != null)
            {
                contextItem.Fields[FieldNameTitle].SetValue(SplitCamelCase(contextItem.Name), false);
            }
        }

        /// <summary>The split camel case.</summary>
        /// <param name="input">The input.</param>
        /// <returns>The <see cref="string"/>.</returns>
        private static string SplitCamelCase(string input)
        {
            return Regex.Replace(input, "([A-Z])", " $1", RegexOptions.Compiled).Trim();
        }
    }
}

Patch in the following config:

<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
  <events timingLevel="custom">
      <event name="item:saving">
        <handler patch:instead="*[@type='Sitecore.Tasks.ItemEventHandler, Sitecore.Kernel']" type="Aceik.Framework.SC.Extensions.Events.ItemOnSavingEvents, Aceik.Framework.SC.Extensions" method="OnItemSaving"/>
      </event>
    </events>
  </sitecore>
</configuration>

As the description is being auto generated it restricts your ability to add a custom description, however you can add additional information to the display title by adding help text. This blog explains it well: http://firebreaksice.com/sitecore-item-and-field-names/