Reserve space for app-injected content
Reserve space for app-injected content using App Blocks with Custom CSS to set min-height, or position injected content next to static content to avoid shifting visible content.
The typical sequence is: the browser renders the initial page from theme HTML, app JavaScript loads and executes, the script injects new content into the DOM, and the browser must reflow layout and shift existing content to make room. This shift is recorded as CLS and can cause customers to lose their place or click on the wrong element. Common sources include review and rating widgets, promotional banners, payment option displays, inventory level indicators, and dynamic pricing.
The app widget types that most commonly cause CLS are:
- Star rating and review widgets below the product title.
- Trust badge and payment icon rows.
- Shipping estimate and delivery date widgets.
Cookie consent banners and chat bubbles come from app embed blocks, which Shopify injects before the closing </head> or </body> tag instead of into a section, so the section-scoped CSS below doesn't reach them. They shift layout only when they render in normal flow, so a position: fixed overlay is the fix there rather than reserved space.
Modern apps use app blocks, which you can target with Custom CSS to reserve space before content arrives. Alternatively, position injected content strategically so that shifts don't affect visible content. Add app blocks to the theme, select the parent section, add Custom CSS to the section, and target the .shopify-app-block selector to set min-height or explicit dimensions.
Allow the @app block type in the section schema:
Then add Custom CSS to the section that hosts the block:
Shopify wraps every block in an element that carries the shopify-block class, and adds a second class, shopify-app-block, to blocks that come from an app. Target .shopify-app-block so that the reserved space applies to app content and not to the theme's own blocks, which share the broader shopify-block class. Scope the selector to the section or container that holds the block, because a min-height applied to every app block on the page reserves space where none is needed.
The same wrapper also carries an id attribute of shopify-block- followed by the block ID, such as id="shopify-block-1a2b3c4d", so you can reserve space for one specific block instead of every app block in the container. Inspect the rendered DOM in Chrome DevTools to confirm the wrapper before you write the selector, because the element's tag and any extra classes come from the app and the theme.
For different widget types, use different min-height values:
- Star ratings and small badges:
min-height: 24pxto30px. - Review summaries and trust badge rows:
min-height: 48pxto60px. - Full review widgets with content:
min-height: 80pxto200px.
Anchor to Design alternativesDesign alternatives
- Position injected content next to static content of equal or greater height. The shift still occurs, but because the surrounding content is taller, nothing visible moves.
- Place injected content at the bottom of a section or outside the initial viewport. Shifts below the viewport don't count toward CLS.
- Use a placeholder skeleton, such as a gray box or shimmer animation, that matches the expected widget dimensions. This signals that content is loading while preventing layout shift.
Anchor to ExamplesExamples
Anchor to Reserve space for a review widget on the product pageReserve space for a review widget on the product page
A star rating app block sits under the product title, above the price. It renders about 28 px tall on desktop and wraps to two lines on mobile. Reserve the space it needs at each breakpoint, and target the specific block so that other app blocks in the same container aren't padded:
Pair the reserved space with a placeholder so that the gap reads as loading content rather than a rendering bug. :empty matches only a wrapper with no child nodes at all, including whitespace, so confirm in DevTools that the placeholder disappears after the app renders:
Anchor to Measuring the right ,[object Object]Measuring the right min-height
min-heightTo determine the correct value:
- Install the app on a development store.
- Open Chrome DevTools, and inspect the rendered app widget.
- Note the
offsetHeightof the widget's container element. - Use that value (or slightly larger) as your
min-height. - Test on mobile too, because many widgets render at different heights on smaller screens.
Anchor to Any framework's container elementsAny framework's container elements
The min-height pattern applies to any framework's container elements. If you're rendering content with a JavaScript framework inside a Liquid theme, then set the container's dimensions before the framework renders content:
Anchor to TestingTesting
- The Chrome DevTools Performance panel shows layout shifts. Look for the Layout Shifts track, or use its Insights tab for an automated analysis.
- Use the Rendering tab and Layout Shift Regions to visually highlight shifted content in real time.
- Visually observe with network throttling enabled.
- Compare before and after the
min-heightimplementation.
Anchor to ReferencesReferences
- How to optimize Cumulative Layout Shift (CLS) on Shopify sites
- Prevent dialogs from hijacking LCP
- Prevent image layout shift