Minimizing CLS to Speed Up Page Loads

To make recommendations load instantly and avoid hurting Core Web Vitals, reserve space in advance, show lightweight placeholders, and replace them with data atomically. Below is a step-by-step guide for a web developer to prevent layout shift (CLS) and keep LCP/INP in the green.

Stage 1: Identify and Reserve Space

Goal: The browser must know an element's dimensions before its content loads.

StepActionRecommendations
1.1. Reserve dimensionsAlways set the width and height attributes for asynchronously loaded elements (images, videos, ad slots).Use aspect-ratio. If only the aspect ratio is known, use the "padding hack" for responsive layouts.
1.2. Container stylingCreate a fixed container (wrapper) for dynamic content that does not change its size.Set an expected height via min-height for elements with variable text length (product cards, comments, etc.).

Stage 2: Display a Placeholder (Skeleton)

Goal: Fill the reserved space with a visual mockup until data arrives.

StepActionRecommendations
2.1. Show the skeletonInside the container from step 1.2, render a visual skeleton screen immediately.The skeleton should match the final content's size and proportions (e.g., areas for a title, avatar, and several lines of text).
2.2. Style the skeletonStyle the skeleton to mimic the upcoming blocks (light-gray rectangles, subtle animation).Use plain CSS or ready-made skeleton libraries. Important: the skeleton block sizes must match the final sizes to avoid shifts.

Stage 3: Work With Data and Replacement

Goal: Smoothly replace the placeholder with real data.

StepActionRecommendations
3.1. Fetch dataSend an API request to retrieve real data.Use modern mechanisms (e.g., fetch or libraries like Axios).
3.2. Render dataOnce data is received, build the real content element (e.g., a product card) in memory (or as a hidden element).Ensure the real content occupies the same space as the skeleton, or at least does not exceed the space reserved in step 1.2.
3.3. Atomic swapReplace the skeleton with the real content as soon as it is ready.Use a single DOM operation (node replacement or a fast class toggle) to minimize the time between removing the skeleton and showing the data.

Stage 4: Handling missing data

Goal: Prevent shifts if dynamic content is empty.

StepActionRecommendations
4.1. No data returnedIf the API returns an empty dataset and the element isn't needed, choose one of the options below.Select an option:
4.2. Option A: RemoveRemove the reserved container (from Stage 1) and the skeleton.CLS risk: If the element was above the fold, removal may cause a small CLS for elements below. Do this only if the container is small and impact is minor.
4.3. Option B: FallbackReplace the skeleton with a "no data" message (e.g., "No products available" or "No comments yet").Best option: Preserves the reserved space (Stage 1), prevents jumps, and provides helpful feedback. The message should occupy the same area.

General Recommendations to prevent CLS

  • Avoid inserting content above existing elements: Never inject dynamic content (banners, pop-ups) at the top of the page after the main content has loaded. If necessary, use modals or floating, fixed-position elements (position: fixed) that do not affect layout.
  • Web fonts: Use font-display: swap, and if possible load a local version of the font or use size-adjust to reduce FOUT (Flash of Unstyled Text) or FOIT (Flash of Invisible Text), which can also cause CLS.
  • Transforms instead of layout properties: For motion, prefer transform: translate() over layout-triggering properties (top, left, width, height).