Google tweaks its core search algorithm on an ongoing basis, and soon, overall web performance will play a bigger factor in page rankings. Google will evaluate the following three metrics collected from real users when determining rankings:
These metrics together comprise Google’s Web Vitals initiative. In this post, we will discuss CLS specifically, with special focus on how to prevent layout shifts in Next.js apps.
Google defines Cumulative Layout Shift as follows:
CLS measures the sum total of all individual layout shift scores for every unexpected layout shift that occurs during the entire lifespan of the page. A layout shift occurs any time a visible element changes its position from one rendered frame to the next.
In the simplest terms, CLS is a measure of the shifts between components when a page loads. It’s ultimately represented by a layout shift score, calculated as below:
layout shift score = impact fraction * distance fraction
Where impact fraction is the sum of the area of impact of all the unstable elements between two frames, and distance fraction is the distance of an element’s or component’s movement between two frames.
Layout shifts are commonly caused by ads/embeds, dynamically injected content, and images without dimensions. Let’s explore how to handle CLS for each of these cases.
Ads are an important source of income for many webpages and can’t be ignored. When a new ad container is inserted into the DOM, however, it can cause a serious reflow or layout shift; it may move parts of the entire webpage. In addition to issues with insertion, if the ad container resizes due to its content or the ad library, this also creates additional friction.
We can fixed this by following the techniques below:
Dynamic content is another major cause of layout shifts. Without placeholders, such content or components (e.g., an “accept cookies” container) can cause a serious layout shift. Even with placeholder containers, an increase or decrease in the size of the expected content will cause a layout shift.
Dynamic content can also be the result of a successful network call/API response. This data will initially be empty and load following a successful network call, causing layout shifts.
In addition to an optimized placeholder container, a skeleton UI is an excellent fix for dynamic content. Consider an ecommerce product page as an example. A skeleton component of each product item will create a clean layout, and after a successful API response, these skeleton components are updated without major layout shifts.
Images without dimensions are one of the most common yet easily fixable causes of CLS. An image without predefined dimensions won’t take up any space on the screen until it loads. Thus, when it does load, it shifts all the elements around it, thereby causing a domino effect of layout shifts.
We can fix this by adding height
and width
attributes to the image tags:
<img src="example.jpg" width="640" height="360" alt="Alt text">
This is a fixed height and width, however, which would still cause a layout shift in a responsive design. Hence, in responsive design, fixed dimensions aren’t much help. Also, since CSS has become a much more desirable way to size images, we need a better solution.
This is where the CSS aspect-ratio
property comes into picture. We still pass on height
and width
attributes but use aspect-ratio
to maintain the ratio between those attributes.
<!-- set a 640:360 i.e a 16:9 - aspect ratio --> <img src="example.jpg" width="640" height="360" alt="Alt text"> <!-- CSS --> <style> img { aspect-ratio: attr(width) / attr(height); } </style>
Here we maintain the aspect ratio as a constant value irrespective of the actual image size, which helps us to better fight CLS. Unfortunately, all too often, developers either ignore or deprioritize this relatively simple optimization.
Next.js is a progressive framework built on React that aims to implement defaults and constraints to fix the issues we discussed above. Let’s see how to leverage the above techniques with Next.js and how they’re supported out of the box.
As discussed above, images without dimensions are a major cause of layout shifts. To fight this, Next.js has an Image
component, which is a wrapper over the img
element.
What does this component do, exactly?
height
and width
properties are both optional in the img
tag. As a result, many developers tend to ignore it, causing a bad CLS score. The Next.js Image
component makes height
and width
required props, keeping the developer honest. It also maintains aspect-ratio
out of the boxImage
component has magical support for srcsets
, thereby enabling a smooth experience across devices and viewports of different sizesHere’s an example of the syntax:
<Image src="/example.png" alt="Alt text" width={500} height={500} />
Though Next.js doesn’t have out-of-the-box support for ads, we can leverage the Layout
component to better handle layout shifts. Next.js lets us create a Layout
component that can be used as a basic layout/structure for all the pages in the app.
Ads are generally placed in the same location across all pages for a given web app. We can create a Layout
component with placeholder containers for ads and include it in the _app.js
file. This enables ad containers on every page, and when the ad loads, there should be minimal to no layout shifts.
We can also leverage this for a skeleton UI depending, on the type of web application. See the syntax below:
# Layout.jsx export const Layout = () => ( <div> <AdContainer /> <Header /> <LeftMenu /> </div> ) # _app.js import { Layout } from '../components/Layout' export default function MyApp({ Component, pageProps }) { return ( <Layout> <Component {...pageProps} /> </Layout> ) }
In this way, we can handle layout shifts for the whole application in single point.
If you are using styled-components with Next.js and didn’t pay close enough attention, there will be huge layout shifts. This is because Next.js is SSR by default, and therefore, it loads JavaScript to the client with base styles. styled-components, on the other hand, is executed at runtime in the browser.
This forces new styles onto the webpage, causing repaint and reflow. To avoid this, use babel-plugin-styled-components
and make use of the styled-components ServerStyleSheet
in the _document.js
to apply styles on the server side, avoiding reflows after rendering on the browser.
By following the above techniques, we can drastically reduce layout shifts in Next.js apps. This is necessary to maintain good SEO, but more than that, it creates a smoother and more pleasing user experience on our web apps.
Debugging Next applications can be difficult, especially when users experience issues that are difficult to reproduce. If you’re interested in monitoring and tracking state, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, try LogRocket.
LogRocket is like a DVR for web and mobile apps, recording literally everything that happens on your Next.js app. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app's performance, reporting with metrics like client CPU load, client memory usage, and more.
The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.
Modernize how you debug your Next.js apps — start monitoring for free.
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowBuild scalable admin dashboards with Filament and Laravel using Form Builder, Notifications, and Actions for clean, interactive panels.
Break down the parts of a URL and explore APIs for working with them in JavaScript, parsing them, building query strings, checking their validity, etc.
In this guide, explore lazy loading and error loading as two techniques for fetching data in React apps.
Deno is a popular JavaScript runtime, and it recently launched version 2.0 with several new features, bug fixes, and improvements […]