Speed Lab: cookie banners and layout shift

A consent banner that pushes the page, one with reserved space, an overlay and one in the HTML, tested for layout shift, first paint and the largest paint.

IntermediateHow-to6 min readUpdated October 30, 2026

In this guide
  1. The question
  2. The page and the banner
    1. The five variants
  3. Results
    1. Speed Index points the wrong way
  4. The placeholder: reserve the real height
  5. The overlay and the banner in the HTML
  6. What this test does not show
  7. What to do
  8. Mistakes to avoid
  9. Check your own page
  10. Sources

The question

Consent notices are everywhere. The 2025 Web Almanac calls cookie consent dialogs, often run by a consent management platform, a near-universal feature of the modern web. They are usually added by a script, after the page has started to draw. That is the pattern that web.dev's guide to layout shift warns about: user interface that pops in at the top or bottom of the viewport, such as a notice about cookies, and pushes the content that is already there. Its advice is to reserve enough space in advance, or to keep the element out of the flow of the page by overlaying it. I tested both, and two other placements.

The page and the banner

The page has a header, a heading, a hero image of 1,200 by 675 pixels (189,585 bytes, with width and height set), two short paragraphs and a style sheet. It transfers 187 KiB in four requests, one of them the favicon. The banner is a pale yellow box with a short paragraph and two buttons. Its height depends on the width of the screen: 180 px on the 412 px test phone, and also at 360 px, and 132 px at 768 px.

I ran each variant nine times, in turns, with Lighthouse's mobile settings and a local server, as described in how the Speed Lab tests are run. Where a script adds the banner, it does so 800 ms after it runs, which is about 1.4 seconds into the load.

The five variants

<!-- a. no banner -->

<!-- b. pushed in: a script inserts the banner at the top of the body, in the flow of the page -->
<script>setTimeout(() => document.body.insertAdjacentHTML('afterbegin', bannerHtml), 800);</script>

<!-- c. a placeholder: an empty box of 150 px is in the HTML, and the script fills it -->
<div id="slot" class="slot"></div>   <!-- .slot { min-height: 150px } -->

<!-- d. an overlay: the script inserts the banner as a box fixed along the bottom of the screen -->
<!-- .cookie.fixed { position: fixed; left: 0; right: 0; bottom: 0; z-index: 10 } -->

<!-- e. in the HTML: the banner is written into the page at the top, with no script -->

Results

VariantFirst paintLargest Contentful PaintCumulative Layout Shift
a. No banner1,224 ms (1,213 to 1,234)2,213 ms (2,210 to 2,225)0
b. Pushed in1,222 ms (1,210 to 1,236)2,215 ms (2,204 to 2,227)0.188
c. Placeholder of 150 px1,217 ms (1,207 to 1,241)2,216 ms (2,207 to 2,231)0.025
d. Overlay1,226 ms (1,205 to 1,244)2,215 ms (2,206 to 2,233)0
e. In the HTML1,230 ms (1,207 to 1,269)2,222 ms (2,206 to 2,255)0

The layout shift scores were the same in all nine runs of each variant. Total Blocking Time was 0 throughout.

Four things stand out.

  1. A banner pushed in after load scored 0.188. The good mark is 0.1 or less, and the page has nothing else wrong. The banner is 180 px tall on a screen 823 px tall, which moved most of what was visible by more than a fifth of the screen.
  2. The first paint and the Largest Contentful Paint did not move. The banner appears after the first paint, and the hero image is larger than the banner, so it stays the largest element. The cost of this banner is all in layout shift.
  3. A placeholder helps only if it is the right size. Mine was 150 px, and the banner is 180 px on a phone, so the page still moved 30 px: a score of 0.025.
  4. The overlay and the banner in the HTML did not shift anything.

Speed Index points the wrong way

Speed Index was 1,854 ms with no banner, 1,934 pushed in, 1,923 with the placeholder, 1,915 with the overlay and 1,632 with the banner in the HTML. The lowest figure belongs to the page where the banner is drawn at the start, because Speed Index rewards filling the screen early, whatever fills it. Use it with care when judging a banner.

The placeholder: reserve the real height

The placeholder cannot be a guess. A banner's height changes with the width of the screen, because its text wraps. At 412 px mine is 180 px tall, and at 768 px it is 132 px. A single reserved height is too short for one of them or wastes space on the other. I changed the placeholder to match each, 180 px up to 700 px wide and 132 px above:

.slot { min-height: 180px; }
@media (min-width: 700px) { .slot { min-height: 132px; } }

In a separate check in Chromium, using a layout shift observer, the pushed-in banner scored 0.188 at 412 px and 0.143 at 768 px, the first matching Lighthouse. With the matched placeholder both scored 0. The reserved height must be checked at the widths your visitors use, and again whenever the banner's text or design changes. A banner that changes language, or a text that wraps to a third line on a narrow phone, changes the height.

The overlay and the banner in the HTML

An overlay along the bottom of the screen takes no space in the page, so nothing moves. Its price is that it covers part of the page until the visitor chooses, and it must still be reachable with a keyboard and a screen reader. Do not let it cover the thing the visitor needs most.

A banner written into the HTML takes its space from the start, so there is no shift either. It needs a way to know whether to show it. A server that reads whether the visitor has already chosen can write the banner or leave it out, and the page is complete on arrival. A script that decides in the browser has to add the banner later, so it needs reserved space or an overlay.

What this test does not show

What to do

  1. Write the banner into the HTML if your setup lets you.
  2. If it must be added later, reserve its height for each screen width, or overlay it at the edge of the screen.
  3. Check the height at 360, 412 and 768 px, in each language, and again after a change.
  4. Load the consent script without blocking the page, with defer or async. See defer, async and modules.
  5. Test with the banner showing. Use a private window or clear your cookies, because with consent given the banner never appears, and the test will look clean.
  6. Watch the real-visitor Cumulative Layout Shift after any change. See how to fix CLS.

Mistakes to avoid

Check your own page

Open the free audit in a private window and read the layout shift culprits. Then load your page on a phone-sized screen with the cookies cleared, and watch the moment the banner appears. If the page jumps, apply the steps above and run the audit again.

Sources

Start here

Free website audit

Live Google data for any public page: mobile and desktop.

Mobile + desktop · Lighthouse lab test · Real-user Chrome data when Google has it

Rather have this done for you? Speed optimization · Book a call

Related guides

All guides →