Bits of Xperience: Reducing CLS when Rendering Images
Tags: xperience, kentico, csharp, html
Published: 2022-05-31 16:10:05

---
title: Bits of Xperience: Reducing CLS when Rendering Images
published: true
description: Learn how to reduce Cumulative Layout Shift (CLS) when rendering images in Kentico Xperience applications
tags: xperience, kentico, csharp, html
series: Bits of Xperience
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/91a47vt9de7x1jkcfrl8.jpg
---

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](https://web.dev/cls/)? 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](https://web.dev/cls-web-tooling/), 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:

```html
<img src="@Model.ImagePath">
```

This will all but guarantee CLS in most situations 😭.

However, [browsers have been using width and height attributes](https://twitter.com/jensimmons/status/1405528938147848204) 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](https://www.smashingmagazine.com/2020/03/setting-height-width-images-important-again/) and it's follow up [How To Fix Cumulative Layout Shift (CLS) Issues](https://www.smashingmagazine.com/2021/06/how-to-fix-cumulative-layout-shift-issues/#set-width-and-heights-on-images-and-iframes)

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](https://github.com/wiredviews/xperience-svg-media-dimensions) library to do this for us automatically!

## Image Dimensions and Media Files

The [Media Library](https://docs.xperience.io/x/UwqRBg) 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8b6e3rujkfkk0yjpqn9n.png)

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/845c9k6tpysrpx30yy8s.png)

Retrieving an image from the media library by full path isn't a great developer experience, so fortunately [@mattnield](https://dev.to/mattnield) wrote a [Regex several years ago](https://stackoverflow.com/questions/47024602/how-can-i-get-guid-from-permanent-url-media-selector/47026186#47026186) 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:

```csharp
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 ...
```

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:

```csharp
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; }
}
```

We can render our image in Razor as follows:

```html
<img src="@Model.Path"
     alt="@Model.AltText"
     title="@Model.Title"
     width="@Model.Width"
     height="@Model.Height">
```

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](https://www.wiredviews.com/news/2022/03/7-things-you-can-t-skip-when-building-a-website) 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

- [Twitter: Jen Simmons - Images and CLS](https://twitter.com/jensimmons/status/1405528938147848204)
- [Smashing Magazine: Setting Height And Width On Images Is Important Again](https://www.smashingmagazine.com/2020/03/setting-height-width-images-important-again/)
- [Smashing Magazine: How To Fix Cumulative Layout Shift (CLS) Issues](https://www.smashingmagazine.com/2021/06/how-to-fix-cumulative-layout-shift-issues/)
- [Kentico Xperience: Media Library](https://docs.xperience.io/x/UwqRBg)
- [Kentico Xperience: Displaying content from media libraries](https://docs.xperience.io/x/LA2RBg)
- [MDN: Aspect-ratio CSS property](https://developer.mozilla.org/en-US/docs/Web/CSS/aspect-ratio)

---

We've put together a list over on [Kentico's GitHub account](https://github.com/Kentico/Home/blob/master/RESOURCES.md) of developer resources. Go check it out!

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

{% tag kentico %}

{% tag xperience %}

Or my [Kentico Xperience blog series](https://dev.to/seangwright/series), like:

- [Kentico Xperience Xplorations](https://dev.to/seangwright/series/8185)
- [Kentico Xperience MVC Widget Experiments](https://dev.to/seangwright/series/9483)
- [Kentico Xperience Design Patterns](https://dev.to/seangwright/series/10963)