Skip to main navigation Skip to main content

Start a project.

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

Conditional asset loading in HTTP2

What we think.

Conditional asset loading in HTTP2


In the world of front-end development, we are driven to near madness by our obsession with performance. Time to first paint, page load and smoothness are all make-or-break factors when exploring new technologies and building new projects. For a long time, CMSs have been seen as the "big lumbering oafs" of performance. Not particularly well optimised, very little support for cutting edge web technologies and the worst part is the bloat they inevitably force into your markup.

Well we're not here to solve any of that, but one of the greatest hurdles to perceived website speed is asset loading. Back in the HTTP/1.1 days every CSS file and every JS file would be minified and bundled, being served as one big request because that was easiest to manage, and faster. Along came HTTP/2 with streaming and server push and suddenly that didn't seem like the best practice anymore.

Since then, we've either turned to bundling for each view, dynamic loading of files, or any of the numerous other methods to speed up perceived page load. But that's usually for web apps. The humble CMS has maintained its ubiquity and importance in the world of web development, but not necessarily caught up to the new demands on performance.

Kentico is no exception to the regular. Its headed version takes no interest in asset loading and instead leaves it up to you. That's all well and good, but it also doesn't help you to do it in any sort of efficient way. So for most people, minification and bundling is probably still the best (and easiest) bet. For a bunch of developers though, we wanted better.

The site of the Australian Red Cross is built almost entirely with widgets, the only few exceptions are the header and footer. On any one page there could be one widget, a few different widgets, or the entire catalogue of widgets. Each one loads one or more assets of CSS or JS, a few of which are shared across multiple widgets. This complex dependency graph is most easily solved by loading everything at once, but when we get down to saving those few KBs, we can definitely do better.

Instead, a system was developed to specify in each widget which assets were required, group these into an array with only distinct assets, then load each asset separately. Essentially what this gives us is, per page, the minimum list of assets required to correctly render a page. Then we can load them how we like, in this specific case through a CDN for even speedier access.

protected override void OnPreRender(EventArgs e)
{
    base.OnPreRender(e);
    var styles = new List
    {
        "/path/to/file.css"
    };
    CommonFunctions.AddPageStyles(styles, Page, "all");
}

The code above is used to add a stylesheet/JS file from a widget to the global list and is added on each web part.

public static void AddPageStyles(List paths, Page currentPage, string mediaType)
{
    foreach (string stylePath in paths)
    {
        var hashIdGeneratedFromPath = stylePath.GetHashCode().ToString();
        var control = currentPage.Header.FindControl(hashIdGeneratedFromPath);
        if (control == null)
       {
           // if external keep same url
           string url = string.Empty;
           if (stylePath.StartsWith("http"))
           {
               url = stylePath;
            }
            else
            {
               url = ("~" + stylePath).ToHtmlString();
           }
           control = new LiteralControl(string.Format(" ", url, mediaType == null ? "" : $"media='{mediaType}'"));
            control.ID = hashIdGeneratedFromPath;
            currentPage.Header.Controls.Add(control);
        }
    }
}

This method will check whether the stylesheet already exists using a hash generated from the path, and if not, create a new control as a style tag for the Header.

This is definitely not the end of load performance either, but it's certainly a solid start, and given the wide variety of widgets available, can save over 50KB for a user just visiting the homepage. Performance will always be a driving factor on the web, and anything we can do to encourage innovation is one of the best things we can do as developers for this ever-changing market.