Still More Stuff
to go Faster

Jul 25

Introduction

This is my third post of ideas to consider to help make your pages load and run faster. Some will help some projects while others may not be quite relevant. There will be some that contradict previous tips. But hopefully, you will find a few to improve your sites perfromance.

In this post, I am looking at tips to improve Google's Page Insights performance results.

Today there is a lot of focus on the Core Web Vitals. You should read up about these first to get an idea about what you should be caring about as well as helping to make reference to some of the terms I use in this article.

Loading Fonts

I am a bit surprised I haven't mentioned this one in my previous 'Go Faster' posts.

As a recap, Preload your fonts as they will be loaded when required slowing down the CSS parsing routine. So get the font file loaded early.

But you may still run into an issue in the reports about a "flash of invisible text" or FOIT. This is where the font isn't quite ready and so the text is 'blocked/hidden' until it is and then it jumps into the page. This is not so great as it affects the Cumulative Layout Shift (CLS) by moving the layout. The @Font-face will most likely choose to do this by default.

An alternative is to display a fallback font until the real font is available. This creates a "flash of unstyled text" or FOUT. But this is considered a better (according to Google) experience and avoids the layout shift. To ensure this happens we just need to add a simple directive of 'swap'.

@font-face {
    font-family'MyWebFont';
    srcurl('myfont.woff2') format('woff2')...;
    font-displayswap/* Define how the browser behaves during download */
}

When using Googles fonts you can add to the query string &display=swap to tell Google to add this as a setting in the fonts downloaded into your site.

CLS & Stylesheets

In a previous post, I gave an idea about deferring the loading of your style sheets. I have two considerations to point out on that.

First, you may find that your page loads with unstyled HTML and then flashes in the styles.

Interesting that the tests don't seem to care about this. I suspect that it happens so little today that they didn't consider it. But if they did I am sure it would be considered a bad user experience.

A way to avoid this is to use a good old fashioned flash screen. Create an element that covers the whole screen and hides all the HTML. Just a white background will do it. But make sure you add the styles inline so that they are applied straight away. Finally, add an 'onload' event to hide/remove this overlay.

Here is an example I did for one of my sites.

<div class="preloader" 
     style="backgroundrgb(255, 192,0); 
            positionfixed; 
            top0; 
            right0; 
            bottom0; 
            left0; 
            z-index1000">
    <div class="loader" style="...""></div>
</div>
Ironically by doing this my CLS score was negatively affected. But if I removed the deferring trick and loaded the styles as normal my CLS score was hugely improved but my overall performance score suffered.

What I really need to do is look at splitting my 'required' styles out and loading them first, (even in a style tag or inline) and the others by deferring. But this shows how it's important to test and review repeatedly to see what works and what doesn't work for your site and finding that balance along with what you are prepared to accept.

Images

A common cause of Sitefinity page problems is images uploaded and being used by the content editors.

The first approach is to address and deal with that first. Perhaps consider a third party to help out.

Another important approach is 'Lazy Loading' your images. There are lots of third-party JS scripts that do this but browsers also support this natively along with a decoding attribute.

<img loading="lazy" decoding="async" src="..." />

Do you need that script? By removing that script will improve your performance score.

You can customise the default Sitefinity Image Widgets and add the attribute in. Create a second version so when the widget is used at the top of the page within the viewport, (above the fold), you can use the original but when placed lower down you can add use the customised version with the lazy loading attribute.

What about that 'decoding' attribute?

When the browser loads an image it does so synchronously, meaning the content below it must wait for the image to load before it gets processed and displayed. By adding decoding="async" you tell the browser to carry on and display the image when it is ready. If you must display a long loading image this may be a good option for you.

By default, the browser is set to 'auto' and decides itself. How it decides, I don't know but I would suspect that it does some size checking of the image to then make a decision.

Also, make sure you supply the images height and width attributes to avoid CLS.

You might, like me, also look at 'Next-Gen' image formats like Webp, JPG XL or AVIF. But.... Sitefinity doesn't support these. Good news is, that SF 14.0 will support Webp which will be a great addition. The others still lack a lot of instrusty support at present but keep an eye on the development here. Will it be JPG XL or AVIF as the winner.

Image Height and Width Attributes

If a browser does not know how much space an image will take then this can be a big contributor to CLS as it injects and moves the layout around for the final image to fit in.

A Sitefinity image has these properties but the default image widget does not provide them. If you set a custom size then you can get access to these properties via @Model.CustomSize.Height/Width. If you want access to these details without requiring the editors to set these values then you need to override the widgets view and model.

Create your extended view model by inheriting the Sitefinity view model.

public class CustomImageViewModel : ImageViewModel
{
    public Int32 Width { getset; }
    public Int32 Height { getset; }
}

Create your own extended custom model.

[TypeConverter(typeof(ExpandableObjectConverter))]
public class CustomImageModel : ImageModel
{
    public override ImageViewModel GetViewModel()
    {
        var viewModel = base.GetViewModel();
        var sfImage = viewModel.Item.DataItem as Telerik.Sitefinity.Libraries.Model.Image;
 
        Int32 origanalWidth = sfImage.Width;
        Int32 origanalHeight = sfImage.Height;
 
        var customViewModel = new CustomImageViewModel
        {
            AlternativeText = viewModel.AlternativeText,
            CssClass = viewModel.CssClass,
            ...
            Width = origanalWidth,
            Height = origanalHeight,
        };
 
        return customViewModel;
    }
}

Register your new image model.

private void BootstrapperOnBootstrapped(object sender, EventArgs e)
{
    FrontendModule.Current.DependencyResolver.Rebind<IImageModel>().To<CustomImageModel>();
}

Finally, in the Image Widget view, you need to cast the model to your custom one.

var customViewModel = Model as CustomImageViewModel;

Stylesheet Media References

You will get a degraded score if you use media="any" when specifying your style sheet. Be more specific and use media="screen".

Hero Images

Most designs have a large hero image at the top and these can be our biggest assets on the page and so we want to get these loaded and ready as soon as possible. The way to do this is using preload.

The problem is that in our CMS we don't know what the user is going to be selecting in the 'Hero Widget' we create for them. The solution is to inject this into our header server-side and we can do this in our razor view.

@using Telerik.Sitefinity.Frontend.Mvc.Infrastructure
 
@{
    var pageHandler = this.ViewContext.HttpContext.Handler.GetPageHandler();
    System.Web.UI.LiteralControl preload = new System.Web.UI.LiteralControl();
    preload.Text = String.Format("<link rel=\"preload\" href=\"{0}\" as=\"image\" />", Model.Hero.Url);
    pageHandler.Header.Controls.Add(preload);
}

Lazy Load Content

This one was new for me recently. The CSS property content-visibility. Here is a plagiarised intro on it.

The content-visibility property might be one of the most impactful new CSS properties for improving page load performance. content-visibility enables the user agent to skip an element's rendering work, including layout and painting until it is needed. Because rendering is skipped, if a large portion of your content is off-screen, leveraging the content-visibility property makes the initial user load much faster. It also allows for faster interactions with the on-screen content.

Sounds cool! So like image lazy loading the final rendering of the element marked will be done as the user approaches/scrolls or calls that content. The content is still loaded in the DOM so you still have access to it but the last part of painting it to the screen is skipped. So if you have an off-screen menu that only gets called sometimes by the user or a bunch of content at the foot of the page that the user doesn't always scroll to then these are good candidates for this property.

I quickly applied this and found an improvement in my score but not quite the whooping impact I was initially expecting. Most likely the type and volume of content will determine how big an impact this will be for your page.

For my test site, I didn't want the content authors to be dealing with the property so I took this approach. My footer, modals and off-screen menu HTML I wrapped with this property. That was an easy no brainer. For my main page, I added a second content placeholder. Anything added in the main was to be rendered straight away. While the second section would have this new property.

<main>
    @Html.SfPlaceHolder("cplMain")
</main>
<section style="content-visibilityautocontain-intrinsic-size1200px;">
    @Html.SfPlaceHolder("cplSection")
</section>

There is that second property, contain-intrinsic-size. This is just like using the width and height properties for images. This indicates to the browser how much height space to reserve for the content. I took an average guess for the site but to be more efficient you will probably need to look at wrapping each widget in this property allowing you to break down the guesses into smaller pieces of your page.

Final note - This is only supported fully in Chrome while other browsers will just ignore it.


Darrin Robertson - Sitefinity Developer

Thanks for reading and feel free to comment - Darrin Robertson

If I was really helpful and you would buy me a coffee if you could, yay! You can.


Leave a comment
Load more comments

Make a Comment

recapcha code