Cart Drawer Performance — Keep Shopify LCP Intact After You Add Upsells

How a slide cart can wreck Largest Contentful Paint, the loading pattern that does not, and the checks to run before and after you install a cart app.

Editorial illustration for Cart Drawer Performance — Keep Shopify LCP Intact After You Add Upsells

Key takeaways

  • A cart drawer should never appear in the initial HTML of the product page. If it does, it is in the LCP path whether you can see it or not.
  • Load the app as a theme app extension, asynchronously, and render the drawer on first open — not on first paint.
  • Images in the drawer (product thumbs, upsell cards) must be lazy. They are off-screen until the shopper adds something.
  • Measure LCP on a product page with the app on and off. If it moved, the drawer is doing work too early.

A cart drawer that costs you 200ms of Largest Contentful Paint is not a conversion feature. It is a tax on every session, including the ones that never open the cart.

The rule is simple: the drawer does not exist until someone needs it. Everything else is implementation detail.

Where drawers show up in the critical path

Three patterns show up in real stores:

  1. The HTML is in the page. The drawer markup is in the initial response, hidden with CSS. Images inside it still decode. Fonts still swap. LCP still pays.
  2. The JavaScript is in the page. A 150KB bundle parses on every product page so that a drawer could open. Parse time is main-thread time.
  3. The app injects into theme.liquid. This is the legacy pattern. It is also the one Shopify has spent years trying to kill, for good reason.

The pattern that does not show up in LCP: a theme app extension, loaded asynchronously, that fetches drawer UI on first open and caches it for the session.

What to measure

Before you install anything, record on a representative product page:

  • Largest Contentful Paint
  • Total blocking time
  • Transfer size of JS on load

Install the app. Repeat the same page, same device, same throttling. If LCP moved by more than a rounding error, the drawer is doing work at the wrong time.

Do not trust a homepage test. Homepages often have a hero image that masks a JS regression. Product pages are where carts actually live.

Images and upsells

Every upsell card is an image request. If those requests start on page load, you have built a carousel the shopper did not ask for. Lazy-load thumbs. Decode on open. If the first open takes 200ms to paint, that is acceptable; it is a click later, not a tax on landing.

The fail-safe

If the app is unreachable, the drawer should not render and the theme's native add-to-cart should still work. A conversion widget that can take down add-to-cart is worse than no widget. Fail safe, always.

Frequently asked questions

Will a cart drawer app slow my store down?

It should not. A well-built drawer loads asynchronously, renders only when opened, and never blocks the initial paint. If your Largest Contentful Paint changes after install, the app is in the critical path and you should replace it.

Does Shopify's theme app extension model guarantee speed?

It removes the worst failure mode — a script injected into theme.liquid that runs on every page. It does not stop an app from downloading a large bundle on load. Check the network panel.

Should I preload the drawer?

No. Preloading a UI the shopper has not opened yet is paying for work you may never need. Fetch on first add-to-cart, then keep it warm for the rest of the session.

What about app pixels and analytics in the cart?

Fire them on open or on offer impression, not on page load. Cart-level analytics that run on every product page are a common source of main-thread work that nobody asked for.

Ninety9 Team

We build 5 conversion apps used by Shopify merchants in Bulgaria and beyond. Everything we write here comes out of what we see in real store data.

Keep reading

Related articles