Core Web Vitals

CLS From Embeds: Trustpilot, Ads and Late Iframes

Review widgets, ad slots, maps and social embeds are the top cause of layout shift on business sites.

5 min read
CLS From Embeds: Trustpilot, Ads and Late Iframes
Core Web VitalsCLSThird Parties
998 words5 min read

The shift you did not write

Your own markup has dimensions on every image and a fixed header. CLS is still 0.2. The Layout Shifts track in DevTools points at a Trustpilot widget, a Google Maps iframe, a YouTube embed, an Instagram feed or an ad slot. Each rendered as zero height, then arrived with real content and pushed everything below it down. You did not write the widget, cannot change its script, and it is costing you the metric.

Embeds shift because their size is decided by a script you do not control, after your page has laid out. The fix is to decide the size yourself, before the script runs, and hold it.

The general pattern: reserve the space

Every embed goes inside a container with a fixed size or a fixed aspect ratio, so the page is laid out with the embed's final dimensions before the embed exists. When the content arrives, it fills the box; nothing moves.

HTML
<div class="embed embed--review" aria-busy="true">
  <!-- widget script injects here -->
</div>
CSS
.embed { position: relative; width: 100%; contain: layout; }
.embed--review { min-height: 140px; }                 /* known height */
.embed--video  { aspect-ratio: 16 / 9; }              /* known ratio */
.embed--map    { aspect-ratio: 4 / 3; }
.embed > iframe { position: absolute; inset: 0; width: 100%; height: 100%; }

contain: layout tells the browser that changes inside the container cannot affect layout outside it, which is both a performance hint and a guard. min-height (not height) lets the widget grow if it must, at the cost of a small shift, while preventing the zero-to-full jump.

The question for each embed is: what size will it be? Measure it once, in the finished state, on a phone and on desktop, and reserve that.

Review widgets: Trustpilot, Google reviews, Yotpo, Reviews.io

These inject a carousel or a badge whose height depends on the layout chosen in the vendor's dashboard. They are the most common CLS source on service-business sites because they sit high on the page.

  • Reserve the widget's measured height per breakpoint. A Trustpilot "micro" badge is a fixed height; a carousel is taller on mobile where cards stack.
  • Load the widget script deferred or after the page is interactive; the reserved box shows a lightweight placeholder (your star rating and count, as plain text, from the last known value) until then.
  • Better: render the rating server-side from the vendor's API (Trustpilot, Google Places and most others provide one), cache it for a day, and drop the client widget entirely. The page shows real stars in real HTML with zero shift and zero third-party script. The widget's only advantage was live updates, which a daily cache approximates.

Maps

A Google Maps iframe has whatever dimensions you give the iframe; the shift comes when the iframe is injected by a script or when a plugin sets the height after load. Give the container an aspect ratio and the iframe width="100%" height="100%" inside it. Then load the iframe on interaction: a static map image (the Static Maps API, or a screenshot) with a "Open map" button that swaps in the iframe. The static image is sized, cached, and loads in a fraction of the time; most visitors only want to see where you are.

Video: YouTube and Vimeo

The iframe embed code includes width and height attributes, which is why a bare embed often does not shift. It shifts when a theme strips the attributes for responsiveness without adding an aspect-ratio box, or when a lazy-load plugin injects the iframe later. Container with aspect-ratio: 16 / 9, iframe filling it, and a facade (the poster image with a play button) that loads the real iframe on click. lite-youtube-embed does this and removes roughly a megabyte of script from the initial load as a bonus.

Social feeds: Instagram, Facebook, X

Feeds render a variable number of items at a variable height. Reserve a min-height for the expected row count and let it grow, or fix the item count and aspect ratio so the height is knowable. Consider whether the live feed is worth it at all: a server-rendered grid of the last six images from the platform's API, cached hourly, is sized, fast, and does not shift.

Ads

Ad slots are the classic case and the solution is the same: a container with the slot's fixed size (min-height: 250px for a 300x250, per breakpoint for responsive units), never collapsing when no ad fills, so unfilled slots show blank space rather than pulling the page up. Most ad platforms document the "reserve the slot" pattern; use it. If a slot's fill rate is low, remove it rather than leave a hole.

Not embeds in the strict sense but the same failure: injected after load, taking space. Fixed-position overlays never shift content. A banner that pushes content needs its space reserved in the initial CSS. Chat bubbles are fixed-position by default and shift nothing; the shift comes from a "we're online" bar some tools inject at the top, which should be disabled or reserved.

Verifying

DevTools Performance panel, record a load on a throttled mobile profile, and check the Layout Shifts track. Each remaining shift names its element. The target is no shift record attributable to an embed, and a CLS under 0.05. In the field, Search Console's CLS grade for the affected page group follows within 28 days.

The rule

An embed's size is your decision, not its script's. Measure it, reserve it, and where you can, replace the script with server-rendered content from the same source. On business sites, the review widget and the map are usually the whole CLS problem, and both have a server-rendered alternative that is also faster. Both are on the checklist of every Core Web Vitals audit we run, because they are where the metric lives on sites whose own code is fine.

All writingHire me for this