DEV Community

Cover image for Bits of Xperience: Reducing CLS when Rendering Images
Sean G. Wright for WiredViews

Posted on • Edited on

Bits of Xperience: Reducing CLS when Rendering Images

Kentico Xperience sites allow marketers and content managers to author content and build pages that display text, images, and anything else that HTML supports 😁.

There are multiple ways to add images to content in Xperience:

  • Embed in Rich Text
  • Custom Page Builder Component (Widget or Section)
  • Rendered with Razor in a Page view or Template

These images can come from multiple sources:

  • Media Library
  • Attachments
  • Meta files (for SKUs)
  • Form Submissions

No matter which rendering approach or content source combination we are using for images on our sites, we'll want to make sure we always render those images with the appropriate HTML attributes, and these days that includes the width and height attributes.

But why 🤔?

📚 What Will We Learn?

  • What is Cumulative Layout Shift (CLS)?
  • How width and height attributes prevent Layout Shift?
  • How do we get this information programmatically?
  • Why should we always retrieve image content from the database?

🌊 Cumulative Layout Shift (CLS)

Have you ever heard of Cumulative Layout Shift? It is defined (by Google) as follows:

CLS is a measure of the largest burst of layout shift scores for every unexpected layout shift that occurs during the entire lifespan of a page.

A layout shift occurs any time a visible element changes its position from one rendered frame to the next. (See below for details on how individual layout shift scores are calculated.)

We've all experienced this on websites. Something on the page finally loads and a button moves, or the text we're reading shifts out of the viewport.

Not only is this extremely annoying 😖, but Google uses it as a quality indicator for websites. More CLS means a worse user experience and Google takes that into account for scoring in PageSpeed Insights, which means it impacts search ranking 😮.

So, now that it's clear we want to reduce CLS on our sites, how does this factor into displaying images with Kentico Xperience?

Reducing CLS with Images and width and height HTML Attributes

Images can be a major cause of CLS 🙁 because the browser doesn't know an image's dimensions until it's been fully downloaded. While the image is being downloaded, the browser's rendering engine doesn't know how much space to allocate for it on the page. Once it finishes downloading, the browser re-renders, moving other content around to give the image the space it needs 😒.

It should be clear now why we shouldn't be rendering images in Xperience with just a src attribute:

<img src="@Model.ImagePath">
Enter fullscreen mode Exit fullscreen mode

This will all but guarantee CLS in most situations 😭.

However, browsers have been using width and height attributes to pre-allocate space for images for awhile now 🧐.

Check out this great article on the topic on Smashing: Setting Height And Width On Images Is Important Again and it's follow up How To Fix Cumulative Layout Shift (CLS) Issues

If we know the width and height values for an image and add those attributes/values when rendering it, we can help prevent CLS by letting the browser know how much space (vertically) the image will need based on the image's aspect ratio computed from these two values 👏🏾.

Getting an Image's Dimensions in Xperience

Thankfully, Xperience stores all uploaded image's dimensions in the database 🙏🏽. However, since there are multiple ways to store and display images in Xperience, we'll need multiple techniques for retrieving an image's dimensions.

Note: Xperience doesn't treat .svg files as images, which means we need to customize its behavior to capture SVG image dimensions when they are uploaded to a site.

We can use the Xperience SVG Media Dimensions library to do this for us automatically!

Image Dimensions and Media Files

The Media Library is probably the best place to store and manage images in Xperience so we'll use it as an example 👍🏿.

To select files from the Media Library for Page Type fields, you'll probably use the Media Selection Form Control:

Media Selection Form Control UI for a Page Type field

This will display a path on the page's Content tab for this control and a preview of the image:

Page Content tab for Media Selection Form Control

Retrieving an image from the media library by full path isn't a great developer experience, so fortunately @mattnield wrote a Regex several years ago that is able to retrieve the media file's FileGUID from the path 💪🏼.

With the FileGUID retrieved, we can query the image's content from the database:

HomePage? page = (await pageRetriever.RetrieveAsync<HomePage>(
   query => query.TopN(1), 
   cancellationToken: token))
   .FirstOrDefault();

if (page is null || 
    string.IsNullOrWhiteSpace(page.Fields.HeroImageMediaFilePath))
{
   return;
}

string pattern = @"[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}";

Guid mediaFileGUID = Regex.Match(
    page.Fields.HeroImageMediaFilePath, 
    pattern, 
    RegexOptions.IgnoreCase).Value;

MediaFileInfo? file = (await mediaFileInfoProvider.Get()
    .WhereEquals(
        nameof(MediaFileInfo.FileGUID), 
        mediaFileGUID)
    .GetEnumerableTypedResultAsync(cancellationToken: token))
    .FirstOrDefault();

if (file is null)
{
   return;
}

// use Media File values ...
Enter fullscreen mode Exit fullscreen mode

Now that we have our full MediaFileInfo we can use all of its data to render our <img> element 🙌.

Assuming we have an ImageViewModel class defined like this:

public class ImageViewModel
{
    public ImageViewModel(
        MediaFileInfo file,
        IMediaFileUrlRetriever urlRetriever)
    {
        Path = urlRetriever.Retrieve(file).RelativePath;
        AltText = file.FileDescription;
        Title = file.FileTitle;
        Width = file.FileImageWidth;
        Height = file.FileImageHeight;
    }

    public string Path { get; }
    public string AltText { get; }
    public string Title { get; }
    public int Width { get; }
    public int Height { get; }
}
Enter fullscreen mode Exit fullscreen mode

We can render our image in Razor as follows:

<img src="@Model.Path"
     alt="@Model.AltText"
     title="@Model.Title"
     width="@Model.Width"
     height="@Model.Height">
Enter fullscreen mode Exit fullscreen mode

Awesome! Layout shift avoided and CLS reduced! 🎉🥳🎊

Always Retrieve the Full Image!

You might think, "This makes sense for images at the top of the page, but what about images at the bottom? They won't impact CLS, so this is a lot of extra work for little benefit."

First, if we display an image as part of a Widget, we don't know where on the page that Widget will be added and different devices' viewports might place that content in different locations on the page. We should code defensively and always retrieve these values from the database 😊.

Second, and far more importantly, we should always retrieve the full image content from the database for accessibility 😎!

All images in Xperience have Title and Description fields, which translate to the title and alt HTML attributes. If we want accessible sites (I'd argue that accessibility is one of the 7 things we can't skip when building a website), then we need to include these values on every informational image we render (background images that are for design don't have the same requirements).

This means we are going to have to retrieve the image content anyway 🤷🏽‍♀️, so it's really no extra work to get the width and height values.

🏁 Conclusion

Cumulative Layout Shift (CLS) is "a measure of the largest burst of layout shift scores for every unexpected layout shift that occurs during the entire lifespan of a page". This measurement represents some of the user experience visitors will have when they visit a page and can negatively impact our page's search rank (SEO) 🙄.

The browser doesn't know how much space to allocate for images until they are fully downloaded, so images often impact CLS on sites. To help prevent Layout Shift for images we can add the image's width and height values as HTML attributes to the <img> element. This tells the browser how much space to allocate for the image based on its aspect ratio 🧠.

To add these values with a Kentico Xperience site, we'll need to retrieve the image's content from the database (as opposed to only the image's path).

To improve the accessibility of our sites, we want to include alt text on our images. This content, authored by content managers and marketers, is stored in the image content in the database 🤗.

So, while adding width and height values to an <img> might add some complexity to our code, we should be retrieving an image's content from the database anyway to ensure our sites are accessible 😉!

As always, thanks for reading 🙏!

References


We've put together a list over on Kentico's GitHub account of developer resources. Go check it out!

If you are looking for additional Kentico content, checkout the Kentico or Xperience tags here on DEV.

#kentico

#xperience

Or my Kentico Xperience blog series, like:

Top comments (0)