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.
| Step | Action | Recommendations |
|---|---|---|
| 1.1. Reserve dimensions | Always 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 styling | Create 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.
| Step | Action | Recommendations |
|---|---|---|
| 2.1. Show the skeleton | Inside 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 skeleton | Style 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.
| Step | Action | Recommendations |
|---|---|---|
| 3.1. Fetch data | Send an API request to retrieve real data. | Use modern mechanisms (e.g., fetch or libraries like Axios). |
| 3.2. Render data | Once 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 swap | Replace 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.
| Step | Action | Recommendations |
|---|---|---|
| 4.1. No data returned | If 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: Remove | Remove 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: Fallback | Replace 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 usesize-adjustto 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).
Updated 1 day ago