If you want to reduce the JavaScript your Shopify app scripts inject, the honest answer is that you delete apps. You don't defer them, you don't lazy-load them, you don't wrap them in requestIdleCallback and call it a day. Deferring a 180KB loyalty widget still costs you the download, the parse, the compile and the execution — you've just moved the pain somewhere the Lighthouse score hides it. Removal is the only change that reliably moves both lab and field numbers, and Black Friday is close enough now that removal has a deadline.
Here's the window. Most US stores freeze theme code two weeks before Black Friday. Uninstalling an app needs to happen earlier than that, because uninstalling is the easy half — the debris it leaves in your theme takes a developer a day or two to clean up and another day to QA. So the last genuinely safe date to pull an app is roughly three weeks before Black Friday. After that you're editing live theme files while paid traffic is already ramping, which is how stores end up with a broken add-to-cart on the one weekend that matters.
Count what renders, not what's installed
App count is a bad metric and merchants keep using it. A store running 28 apps can be fast if 22 of them are admin-side: Avalara or Shopify Tax doing nexus math, a 3PL connector, accounting sync, a returns portal, a PO tool. None of those touch the storefront. Meanwhile a store with nine apps can be slow because six of them render a widget on every single page.
So the first pass is a census. Open the theme editor and look at App embeds in the left panel. Everything toggled on there is loading on every template unless the app is unusually well built. Then search the theme for hardcoded snippets, which we'll come back to, because that's where the ghosts live.
Rule of thumb from doing this a lot: more than five or six apps rendering on the storefront and you are paying for it in Interaction to Next Paint, whatever the PageSpeed number says. The marginal app is rarely the problem. The seventh one is.
How to find which Shopify apps slow down your store
Two methods. One takes four minutes and gets you 70% of the answer, the other takes an afternoon and gets you a defensible list.
Fast version: run Lighthouse on a product page in mobile mode and open the Reduce the impact of third-party code table. It gives you transfer size and main-thread blocking time per vendor, attributed by domain. Sort by blocking time. The top three entries are your audit.
Slow version, and the one we actually run: duplicate the live theme, then toggle app embeds off one at a time on the copy and run Lighthouse three times after each change, taking the median. Nine embeds means ten configurations, thirty runs, about forty minutes including the tea. The reason it's worth doing properly is that third-party scripts interfere with each other — two of them fighting for the main thread at the same moment can cost more together than the sum of their individual blocking times, and you only see that by removing them one at a time.
Add the DevTools Coverage tab for a sanity check. It shows unused bytes per file on first load. A review app shipping 240KB of JavaScript where 85% goes unused on a product page is a widget doing one job with an entire framework behind it.
What each script costs: a worked example
Take a reasonably typical apparel store, mobile product page, Lighthouse mobile throttling. Total Blocking Time comes in at 940ms. The third-party table breaks down roughly like this: live chat 300ms, loyalty and rewards 220ms, a reviews widget 170ms, back-in-stock notifications 90ms, an upsell app 80ms, and the rest spread across Meta pixel, TikTok pixel, Klaviyo and GA4.
The merchant hasn't answered a chat in six weeks — support runs on email. The loyalty program has single-digit redemption. Pull both: 300 + 220 = 520ms of blocking time gone, TBT lands near 420ms. LCP moves too, from about 3.4s to 2.6s, because the hero image request is no longer queued behind 400KB of script competing for the same connection.
Is that worth two days of developer time in late October? Do the math on your own numbers rather than trusting a conversion-rate statistic from a vendor's landing page. If you expect 40,000 sessions across BFCM weekend at 2.1% conversion and a $78 AOV, a tenth of a percentage point of conversion is 40 extra orders, or $3,120. That's the threshold the work has to clear. For a store doing 400,000 sessions it clears easily. For a store doing 6,000 it probably doesn't, and you should spend the week on your email flows instead.
App embed blocks, script tags, and the three hiding places
App code reaches your storefront through three routes, and they behave differently when you uninstall.
- Theme app extensions (app embed blocks and app blocks). These are the modern, well-behaved route. They live outside your theme files and disappear cleanly on uninstall. They still cost you a script.
- Script tags, injected through the API. You can't see these in admin. They're removed when the app that created them is uninstalled, which usually works and occasionally doesn't.
- Hardcoded Liquid, pasted into theme.liquid or a section during onboarding, often by a support agent from the app vendor during a screen-share. This never goes away on its own. This is where the trouble is.
The performance impact of app embed blocks is real but overstated relative to the third category. An embed that loads a deferred 40KB bundle is a nuisance. A snippet from an app you churned off in March, still fetching a script from a vendor's CDN that now returns a 404 after a 5-second DNS timeout, is an outage waiting for peak traffic.
Removing leftover app code from a Shopify theme
Work on a duplicated theme, always. The search order we use:
- Grep theme.liquid for <script src and read every result. Any domain that isn't cdn.shopify.com or your own gets justified or deleted.
- Search the whole theme for the app's handle — "judgeme", "loox", "rebuy", whatever it was. Snippets, sections, and stray <div id="..."> containers all show up.
- Check settings_data.json for orphaned block entries from apps that no longer exist.
- Check Settings → Customer events for custom pixels left behind by agencies and freelancers. We find duplicate Meta pixels here more often than anywhere else, which quietly doubles your reported conversions and makes your BFCM ROAS fiction.
- Look for empty containers with reserved height. Leftover CSS reserving 90px for a review stars block that no longer loads is a Cumulative Layout Shift you're paying for with nothing to show.
Then QA the full path: collection, product, variant switch, cart drawer, checkout, order confirmation. On mobile, in an Instagram in-app browser as well as Safari. We get something wrong on the first pass maybe a third of the time, usually a cart drawer that depended on a global the removed script was defining.
Deferring third-party scripts on Shopify checkout: what's actually possible
Mostly nothing, and that's fine. Since checkout extensibility replaced checkout.liquid, you can't inject arbitrary JavaScript into the Shopify checkout. Additional scripts has been retired in favour of custom pixels and UI extensions. Pixels run in a sandboxed worker, off the main thread, which means they can't block checkout rendering the way they used to.
What they can still do is cost you network requests and, if a vendor's endpoint browns out, hang in the background. So the checkout question isn't about deferral, it's about pruning: open Customer events, read every custom pixel, and delete the ones measuring something nobody reports on. Three pixels doing attribution for channels you no longer buy is three vendor dependencies during the one hour of the year your checkout cannot wobble.
LCP and INP when traffic is eight times normal
Your lab scores barely move under load. Shopify's infrastructure handles the traffic; that part isn't your problem. Field data is a different story, for two reasons that both relate to app scripts.
First, traffic composition changes. BFCM traffic skews heavily toward paid social, which means Instagram and TikTok in-app webviews — slower JavaScript engines, no shared browser cache, often older Android hardware. Your p75 device during BFCM is meaningfully worse than your p75 device in October, so INP degrades even though nothing on your site changed. Every event handler a third-party script attaches is competing for a slower main thread.
Second, your vendors are under peak load too. A reviews widget served from a vendor CDN that normally responds in 80ms might take 600ms on Black Friday morning. If that script is render-blocking or if your LCP element sits below it in the dependency chain, their capacity problem becomes your LCP. Removing the script is the only version of this you control.
Targets to hold: LCP under 2.5s and INP under 200ms at the 75th percentile. If you're at 2.4s today in October on a quiet Tuesday, assume you'll be over 2.5s on Black Friday and build headroom now. More detail on how we approach the whole stack is on our Shopify speed optimization page.
My PageSpeed score dropped after installing an app
That's the normal outcome, not a bug. A storefront app adds at minimum a DNS lookup, a TLS handshake and a script download against a new origin, which is 200–400ms on a cold mobile connection before a single line executes. Scores of 8 to 15 points lost from one widget are routine.
What matters is whether the app earns it. An upsell app adding 4% to AOV earns 12 points. An accessibility overlay widget does not, and while we're here: overlays are heavy, they rely on client-side JavaScript to patch markup problems, and they have not proven to be reliable protection against ADA demand letters. If WCAG compliance is on your risk register — and in the US it should be — fix the underlying templates. The overlay is a script tax you pay for a defence that may not hold.
The calendar, counting back from Black Friday
- T-25 to T-21 days: audit and uninstall. This is the window. Any app you're unsure about, cancel it now and see if anyone notices while you still have time to reinstall.
- T-21 to T-16: clean leftover theme code, remove orphaned pixels, re-run Lighthouse, fix the regressions the cleanup caused.
- T-16 to T-14: full QA on the live theme copy. Publish.
- T-14: code freeze. No theme edits, no new app installs, no "quick" snippet from a vendor's support team. None.
- T-14 onward: monitoring only. Watch field Core Web Vitals daily and keep a rollback theme ready.
The freeze is the part merchants break. Somebody will want to install a countdown timer on the Tuesday before Black Friday. Say no.
Keeping the score after you've fixed it
Speed regresses because apps get installed and nobody re-measures. If you want the October work to still be true in February, you need a score you look at without thinking about it. Our own app SwiftStore scans PageSpeed, fixes what it can automatically and tracks the score over time, which is enough to catch the marketing intern's new widget before it compounds. A weekly Lighthouse CI run against your product template does the same job if you'd rather build it.
And if an app is genuinely load-bearing but badly built, replacing it with purpose-built theme code is often cheaper than it sounds — a bespoke size-chart or bundle builder usually comes in under the annual subscription it replaces, and it ships zero third-party origins. That's the thinking behind most of our custom app and extension work.
If you want a second pair of eyes before the freeze, send us your store URL and we'll run the third-party breakdown and tell you which three things to pull. Takes us about a day. Request the audit here — but do it this week, because the useful window closes when your code does.


