Fixing CLS from Dynamic Content Insertion
A lot of what appears on a modern page is decided after the page has been drawn: the cookie notice, a newsletter prompt, an age check, a personalised message, a headline being tested, the next batch of articles. None of these is a problem in itself.
The shift happens when the new content is slotted into the normal flow of the page, so the text the visitor has started reading is pushed out of the way. This guide shows how to find late insertions and the choice for each: overlay it, reserve its place, decide it on the server, or wait for the visitor to ask for it.
Why late content counts as a shift
Adding something new to a page is not itself a layout shift. What counts is existing, visible content changing position as a result. So a notice that appears in empty space, or on top of the page, costs nothing. The same notice inserted at the top of the document costs a great deal, because every line of text on the screen moves down by its height.
There is an important exception. Shifts within half a second of a click, tap or key press are ignored, because the visitor caused the change and expects it. That means the problem is specifically content that arrives on its own:
- content that appears after a timer, such as a sign-up prompt after 10 seconds;
- content that waits for a network response, such as a consent tool checking the visitor's region;
- content decided in the browser, such as an experiment swapping a headline or a location-based message;
- content that arrives after an interaction but too slowly, such as a "load more" button whose results take a second to come back.
The last one surprises people. The visitor did click, but the insertion happened more than 500 milliseconds later, so the grace period had ended by the time the page moved.
Finding late insertions
Late content is often only shown to new visitors, or only in some regions, so the page may look stable to you because you dismissed everything months ago.
- Open a private window so there are no cookies or stored settings. Run the CLS Debugger and note the shifts and the elements named.
- In Chrome DevTools, record a reload in the Performance panel with slow throttling. Wait at least 15 seconds without touching the page, since timed prompts often appear after a delay, then scroll slowly to the bottom.
- Select each shift and look at what appeared, disappeared or changed size directly above the elements that moved.
- Repeat after accepting cookies, and after refusing them. Consent choices change what loads.
To catch insertions as they happen, you can log every element added to the page after load, alongside the shifts:
new MutationObserver((records) => {
for (const r of records) {
r.addedNodes.forEach((n) => {
if (n.nodeType === 1) console.log('added', Math.round(performance.now()), n);
});
}
}).observe(document.body, { childList: true, subtree: true });
Match the timestamps against the shift times in the Performance panel. An element added a moment before a large shift, above the content that moved, is your culprit.
The fix, step by step
Step 1
List what appears after the first paint
In a private window with slow throttling, record a reload, wait at least 15 seconds without touching anything, then scroll to the bottom. Note every notice, prompt, message or block that appears, disappears or changes after the page first draws, and whether it sits in the normal flow of the page. Compare the list with the elements the CLS Debugger reports, and rank them by the size of the shift each one causes.
Step 2
Turn consent notices into overlays
In
your consent tool's appearance settings, choose a layout that floats over the page, such as a bottom
sheet, a corner box or a centred dialog, rather than a bar that pushes content down. If the notice is
custom-built, give it position: fixed so it sits above the content instead of inside it.
Make sure the preferences panel it opens is also an overlay, and that the notice does not cover the whole
screen on phones.
Step 3
Show prompts and age checks on top of the page
Newsletter sign-ups, discount offers, surveys and age checks should appear as
fixed-position dialogs or slide-ins, never as blocks inserted into the article. If a prompt must sit
inside the content, such as a sign-up box halfway down a post, output its container in the HTML with
its final height so the form fills a space that already exists. Animate any entrance with transform
and opacity, not height or margins.
Step 4
Decide personalised content before the page is sent
Messages based on location, login state, basket contents or an experiment variant
should be chosen on the server or at the CDN edge and included in the HTML, so the first paint is already
correct. If that is not possible, reserve the block's space with a fixed min-height and
fill it later. Avoid testing tools that swap content in the browser after load, and anti-flicker snippets
that hide the whole page while they run.
Step 5
Insert new items only below or on request
For feeds, comments and live updates, never add new entries above what the visitor is reading. Show a small fixed button such as "3 new posts" and insert them when it is tapped. For "load more" buttons, add placeholder rows immediately on click, within the half-second grace period, and fill them when the data arrives. With infinite scroll, append below the viewport and keep the footer out of the loaded area.
Step 6
Give deferred sections an intrinsic size
If you use content-visibility: auto to skip rendering off-screen sections, always
pair it with contain-intrinsic-size, for example contain-intrinsic-size: auto 600px,
so each skipped section keeps a realistic height. Without it the sections collapse to zero and grow as
the visitor scrolls towards them. The auto keyword lets the browser remember the real height
once a section has been rendered for the first time.
Step 7
Retest as a first-time visitor
Clear page and CDN caches, open a new private window and run the CLS Debugger again. Repeat the long, untouched recording and the full scroll, then accept cookies and repeat, then refuse and repeat. Check a phone width, where overlays can cover important content. Over the following weeks, watch real-user CLS, since returning and new visitors see different content and both are included.
Overlay, reserve, render early or wait: choosing for each element
Every late element can be made stable in one of four ways. The right one depends on what the content is for.
| Approach | Best for | How | Watch out for |
|---|---|---|---|
| Overlay | Cookie notices, sign-up prompts, age checks, surveys, chat invitations | position: fixed
or a native <dialog>, entering with a transform | Covering buttons or text on small screens; trapping keyboard focus without a way out |
| Reserve | A sign-up box within an article, a recommendations strip, a block filled from an API | Output the container in the HTML with a fixed height or min-height | Content that sometimes does not arrive, leaving a gap; choose a sensible fallback |
| Render early | Personalised greetings, regional messages, experiment variants, logged-in states | Decide on the server or CDN edge and include in the HTML | Page caching: vary the cache by the deciding factor, or cache a neutral version |
| Wait for the visitor | New items in feeds, more results, expanded details | Insert on click or tap, with instant placeholders if data is slow | Responses slower than half a second still count unless space is added immediately |
A native <dialog> opened with showModal() is drawn in the browser's
top layer, above everything else, so it never pushes content. It also handles focus and the Escape key
for you, which makes prompts more accessible.
.signup-slidein {
position: fixed;
right: 1rem;
bottom: 1rem;
max-width: 22rem;
transform: translateY(120%);
transition: transform 0.3s ease;
}
.signup-slidein.is-open { transform: translateY(0); }
Cookie and consent notices in detail
Consent notices are the most common cause of this problem because they appear on every page for every new visitor, often at the top of the screen. A top bar pushed into the document can easily produce a shift large enough to fail the 0.1 threshold on its own.
- Pick the floating layout. Most consent platforms offer several layouts. Bottom banners, corner boxes and centred dialogs usually float; "push down" or "top bar in page" layouts do not. Check the generated CSS: the notice's outer element should be fixed-position.
- Load the tool early. A consent script loaded late shows its notice late, when the visitor may already be reading or tapping. Load it early in the head, as its vendor recommends.
- Watch the second step. A notice that floats may open a preferences panel that is inserted into the page. Test the whole journey.
- Do not shrink the page instead. Adding padding to the body to make room for a fixed notice after it appears moves every element on the page; that is still a shift.
Getting the notice's content and behaviour right is a separate matter from how it is displayed; see fixing cookie consent for that. This is not legal advice; check your consent approach against current UK GDPR and PECR guidance as amended by the Data (Use and Access) Act 2025.
Experiments and personalisation
Tools that change content in the browser after load, whether for A/B tests, geotargeted offers or "welcome back" messages, cause two linked problems. If they change the page after it has been drawn, you get layout shifts as headlines rewrap and blocks appear. To avoid visible flicker, many of them offer a snippet that hides the whole page until the test has run, which swaps a CLS problem for a slow first paint and worse LCP.
Better options, in order of preference:
- Server-side or edge testing. Assign the variant before the HTML is generated, so each visitor receives a finished page.
- Same-size variants. If you must test in the browser, keep variants the same shape: similar headline lengths, images of the same ratio, buttons of the same height.
- Test below the fold. Changes to content that is not yet visible do not shift anything the visitor can see.
For page caching, remember that content decided on the server must not be cached and served to the wrong visitor. Either vary the cache by the relevant cookie or region, or serve a neutral version from the cache and reserve space for the personalised block.
Stopping new insertions creeping back
- Keep a register of third-party scripts that change the page. Marketing, consent, chat, review and survey tools are added by different teams. List what each one inserts and how it is displayed.
- Set a rule for new tools. Anything that shows content on its own must float over the page or fill a reserved space. Make that part of the sign-off for new tools.
- Test as a stranger regularly. Run the CLS Debugger in a private window after each marketing campaign or tool change, and wait long enough for timed prompts.
- Keep interactive loading snappy. Where content loads after a click, show a placeholder straight away so the change always falls within the half-second grace period.
Widgets rendered by JavaScript into a fixed place on the page, such as reviews or product recommendations, are covered by fixing CLS from late-loaded components, and basket and alert bars by fixing CLS from cart and notification banners.
1. Does a cookie consent banner hurt my CLS score?
Only if it moves other content. A notice inserted into the page, typically as a bar at the top, pushes everything down and can fail the 0.1 threshold on its own because every new visitor sees it. A notice displayed with fixed positioning floats over the page and does not move anything, so it adds nothing to CLS. Most consent tools offer a floating layout in their settings; choose one and check that any preferences panel it opens floats too.
2. Do pop-ups count as layout shift?
A true pop-up, one that appears on top of the page in a fixed-position box or a modal dialog, does not move the content beneath it and so is not a layout shift. Prompts that are inserted into the page itself, such as a sign-up form that appears in the middle of an article after a delay, do push content down and count. The visual design can look similar, so check the element's position in DevTools rather than trusting how it looks.
3. Why does clicking load more still cause layout shift?
Shifts are only excluded if they happen within 500 milliseconds of the click. If your button fetches results from the server and the response takes longer than that, the new items arrive after the grace period and their insertion counts. Add placeholder rows straight away when the button is pressed, so the space changes inside the grace period, then fill them when the data arrives. Also make sure new items are added below the button, not above it.
4. Can A/B testing tools cause CLS?
Yes. Tools that run in the browser change headlines, images or blocks after the original version has been drawn, which moves content if the variants are different sizes. Their anti-flicker snippets avoid visible changes by hiding the page until the test is ready, which hurts loading speed instead. Assigning variants on the server or at the CDN edge avoids both problems. If you must test in the browser, keep each variant the same size as the original.
5. Does removing content from the page cause layout shift?
Yes. When a visible element disappears, everything below it moves up, and that counts in the same way as moving down. Common examples are notices removed by a script once it finds the visitor has already dismissed them, loading messages that vanish, and long text collapsed into a read more link after the page has loaded. Decide what to show before the first paint, using a cookie read on the server, or collapse content in CSS from the start.
6. Does content-visibility auto cause layout shift?
It can if it is used without contain-intrinsic-size. The content-visibility property lets the browser skip rendering sections that are off-screen, but a skipped section has no height by default. As the visitor scrolls towards it, the browser renders it, it grows to its real height and content moves. Setting contain-intrinsic-size to auto with a realistic estimated height gives each skipped section a placeholder size, and the auto keyword remembers the real height after it has been rendered once.
7. Is it better to show a sign-up prompt as a modal or inline?
For layout stability, a modal dialog or a fixed slide-in is safest, because it never moves the content underneath. An inline sign-up box is also fine if its space is already in the HTML when the page loads, so the form fills a gap rather than creating one. What causes shifts is an inline prompt added later by a script. Whichever you choose, make it easy to dismiss and avoid covering the main content on small screens.
The CLS Debugger records every layout shift while your page loads, names the elements involved, calculates the cumulative score and ranks each shift by its impact, so you can see which late notice or block is pushing content aside.
Open CLS Debugger Fixes โ