How I removed website layout shift using a predeploy script

Eugenia Zigisova
Level Up Coding
Published in
5 min readFeb 11, 2021

--

I created my website more than 3 years ago when I was at the beginning of my web developer journey. Recently I checked the repository to make some improvements. It was shocking to see how many flaws I made there. Cumulative layout shift (CLS) was one of the biggest UX problems I had, i.e. the content fetched from the CMS caused unexpected movements on the page.

I am sure that everyone has an annoying experience of sudden changes on the page while it is loading. For example, when you start to read an article and second later text goes away. Or when you are about to click a button but unwittingly click on the ad that just appeared. I had the same problem on my website!

The page loading video captured by WebPageTest

But I managed to solve it :)

Loading placeholders

I had multiple solution ideas in the beginning. The first one was to add content loading placeholders to show that the content is coming. Although such an approach would improve the CLS, it still could cause shifts if the shape of rendered content entries is different from the placeholders. Also, content loading speed doesn’t improve. Moreover, by adding placeholders I would ship additional JavaScript that could slow down the page.

Service Worker caching

Secondly, I tried caching content entries using a service worker. Such an approach allows you to improve the speed and UX for returning page visitors. Unfortunately, it does not impact the first-time page load. To be honest, I don’t expect a lot of returning users on my page because its content doesn’t change regularly. Therefore, I needed to focus on improving the initial page load.

Fetching data before build

In the next brainstorm session, I started to question the necessity of using a CMS. Alternatively, I could manage the content in JSON files in the project repository. Nevertheless, I prefer editing content in a CMS because of the convenient UI and automatic optimization of the images. I came up with the idea of fetching the content before the build. In this case, the data would become part of the build and be served from the website server instead of the CMS. To achieve this, I have created a node script for fetching the data and writing it to the JSON files.

// fetchContent.jsonconst fetchPosts = async () => {
const response = await client
.getEntries({
content_type: "card",
});
writeData(response, "./src/data/posts.json");
};

Note: The client and getEntries are provided by Contentful CMS JavaScript SDK.
In React components, I replaced the requests to the CMS with data import from the file.

import posts from "../data/posts.json";

The data fetching script was added to the npm run predeploy command.

// package.json"scripts": {
"predeploy": "node fetchContent.js && npm run build",
}

I use Netlify as a hosting and Continuous Delivery (CD) service where I can configure a build command. By changing it from the default npm run build to npm run predeploy I have added the “fetch content” script to the CD pipeline that is automatically triggered each time I merge to the main branch.

Such an approach helped me to reduce Cumulative Layout Shift to almost zero! You can see the difference with the naked eye:

The Lighthouse CI that I configured in my website’s project enables the web performance regression testing. Based on the comparison with the previous site version, the predeploy script changes brought a range of positive “side-effects”, namely, a slight decrease in the Largest Contentful paint, Time to Interactive, and Total Blocking Time. Surprisingly, the First Contentful Paint and the overall Speed Index have increased a little bit.

Performance metric diffs generate by Lighthouse CI server

A more detailed manual test made on WebPageTest showed that my website server response time (Time to First Byte) is taking longer than before because it is including the content data to the response. You can see on the chart below that the JavaScript size became larger as the JSON files with the content are imported and bundled together with the React components. This is one of the reasons for the increased First Contentful Paint.

One more positive outcome was the number of requests that dropped from 26 to 16.

CMS triggered deployment

As the requests were moved to the “predeploy” script, the website needs to be rebuilt each time the content changes in the CMS. To avoid any manual steps, the CD pipeline could be triggered automatically using webhooks.

I’m using Contentful CMS that provides such a feature. The setup is done in 2 steps: generating the webhook URL in the Netlify settings and adding this URL to Contentful webhook settings. Whenever new content is published, such an event automatically triggers the build and deploys a new version of the website.

What’s next?

Although I have fixed the Cumulative Layout Shift, I still have a lot to improve. For example, Largest Contentful Paint is around 3 seconds that is a huge number for such a tiny page like mine.

The content of my website doesn’t change dynamically. From the user’s point of view, it is a static page. Despite this, it is rendered on the client-side, serves a lot of unused JavaScript that increases server response time, and requires more CPU than it should. The page loading time and UX would benefit from being generated as a static HTML page before it is deployed on the server. As the result, a client would receive ready to be painted HTML and CSS instead of processing ReactJS code.

Fetching data before building the project is the first step I made towards generating a static site. I will keep you posted about the next steps 🤙🏻

--

--