Shopify App Bloat Is Killing Your Storefront: An Audit Framework That Works
Most Shopify stores we inherit are running 15+ apps, half of which inject scripts into every page load. Here's the audit framework we use to decide what stays, what gets replaced, and what gets ripped out.
Every Shopify store we inherit tells the same story. The merchant installed an app for reviews, another for upsells, two for email, one for currency conversion, a loyalty widget, and a chat bubble — and now the product page takes four seconds to become interactive on a mid-range Android. The merchant blames the theme. The theme is usually fine. The apps are the problem.
This is the audit framework we run before touching a line of Liquid.
Why app bloat is the real performance ceiling
Shopify's platform is fast. The CDN is fast, the storefront renderer is fast, and modern themes like Dawn and its forks ship lean. What slows a store down is the accumulation of third-party JavaScript that each app injects — often via ScriptTag, often render-blocking, often loaded on pages where the app does nothing useful.
We've seen stores where the HTML document is 40 KB and the total JavaScript payload is 2.3 MB. Of that payload, roughly 70% came from six apps the merchant could not remember installing. That's the pattern. Apps are easy to install, invisible to uninstall properly, and almost never audited.
The business cost is measurable. In our experience, cutting a Shopify store's third-party JS by half typically moves LCP by 0.8 – 1.5 seconds on 4G and lifts mobile conversion rate by somewhere in the 3 – 8% range. Not life-changing on its own, but close to free money once you know what to cut.
Step 1: Inventory everything, not just what's in the Apps panel
The Shopify admin shows you installed apps. It does not show you:
- Apps that were uninstalled but left ScriptTags behind (yes, this happens)
- Code injected directly into
theme.liquidby a past developer or agency - Pixel snippets added via Online Store → Preferences
- Apps embedded through Shopify's Web Pixels Manager
- Checkout extensions and Shop Pay apps
Start with a four-source inventory. Pull the installed app list, then grep the theme, then query the ScriptTag API, then open DevTools and watch what actually loads.
# List all ScriptTags registered on the store
curl -X GET \
"https://your-store.myshopify.com/admin/api/2025-01/script_tags.json" \
-H "X-Shopify-Access-Token: $SHOPIFY_ADMIN_TOKEN"
You will almost always find orphans — ScriptTags pointing to domains of apps that are no longer installed. These are safe to delete and often account for 100 – 400 KB of dead weight.
Grep the theme for hardcoded injections
# From the theme root after `shopify theme pull`
grep -rniE "(script src=|googletagmanager|hotjar|clarity|intercom|gorgias|klaviyo|yotpo|judge\.me|loox)" ./layout ./snippets ./sections
This catches the stuff a previous agency pasted into theme.liquid five years ago and never removed when the app was swapped out. We find hardcoded Facebook Pixel copies on roughly one in three stores we audit — usually firing alongside the newer version installed through the pixel manager, double-counting events and skewing attribution.
Step 2: Measure per-app cost, not just total weight
Total page weight is a vanity metric. What you want is: for each app, how much JavaScript does it cost, when does it run, and does it block anything?
Open Chrome DevTools, go to the Performance panel, record a cold load of the product page on a throttled Fast 3G profile with 4x CPU slowdown. Then look at the Network panel grouped by domain. Every third-party domain maps to an app or a service. Add up the transfer size and the main-thread blocking time per domain.
Build a simple scorecard:
| App | JS transferred | Main-thread blocking | Pages it loads on | Business value |
|---|---|---|---|---|
| Reviews app | 180 KB | 240 ms | All | High (SEO + CRO) |
| Currency switcher | 90 KB | 110 ms | All | Low (Shopify Markets does this) |
| Upsell popup | 220 KB | 310 ms | Cart only | Medium |
| Chat widget | 410 KB | 180 ms (deferred) | All | Low (5 chats/week) |
Now you have an argument the merchant can read. "This chat app costs 410 KB and handles five conversations a week" is a conversation ender.
Step 3: Classify every app into keep, replace, defer, or kill
Four buckets, no exceptions.
Keep — the app is doing load-bearing work (reviews with schema markup, your ESP's on-site forms, the subscription engine running recurring orders). It stays. You optimise how it loads, not whether.
Replace — the app duplicates something Shopify now does natively or that a lighter alternative handles better. Currency and language switching is the classic example since Shopify Markets rolled out properly. Basic upsells are now available in Shopify's native bundles. Older review apps can often be swapped for ones that lazy-load and ship schema server-side.
Defer — the app does real work but doesn't need to run before interaction. Chat widgets, exit-intent popups, loyalty widgets, back-in-stock forms. These should load after window.load or on first user interaction, never render-blocking.
Kill — the app isn't used, isn't producing measurable revenue, or is a leftover from an experiment nobody remembers. Uninstall, then confirm the ScriptTag was removed via the API and the theme wasn't modified.
A practical defer pattern
For apps that let you self-host their loader (increasingly common), wrap the injection in an interaction gate:
<script>
(function () {
var loaded = false;
function loadDeferredApps() {
if (loaded) return;
loaded = true;
var s = document.createElement('script');
s.src = 'https://cdn.example-chat-app.com/widget.js';
s.async = true;
document.head.appendChild(s);
}
['mousemove', 'touchstart', 'scroll', 'keydown'].forEach(function (evt) {
window.addEventListener(evt, loadDeferredApps, { once: true, passive: true });
});
setTimeout(loadDeferredApps, 6000);
})();
</script>
This loads the widget on the first user interaction, or after six seconds as a fallback. For a chat widget that most visitors never open, this reclaims hundreds of milliseconds of main-thread time with zero business impact.
Step 4: Pay attention to the checkout
Since checkout.liquid is deprecated and checkout extensibility is the only path forward on Shopify Plus, the audit here is different. You can't inject arbitrary scripts anymore — you install UI extensions and functions. That's a feature, not a limitation. Fewer surfaces for bloat.
But you still want to audit:
- Which checkout UI extensions are installed and whether each one is actually rendering
- Whether your Web Pixels are deduplicated (we routinely find GA4 firing twice through two different apps)
- Whether Shop Pay is enabled, because its tokenised checkout meaningfully improves conversion for repeat buyers
If you're still on legacy checkout.liquid in 2026, that's a separate conversation and a hard deadline. We've written about the migration specifics on the 72Technologies blog if you want the field notes.
Step 5: Set a budget and enforce it
An audit without a budget is a one-time cleanup that re-bloats within six months. Set two numbers and write them into your team's definition of done:
- Third-party JS budget per page template: for most Shopify stores, aim for under 350 KB transferred on the product page and under 250 KB on the home page. These are starting targets, not laws.
- New app approval process: no new app gets installed without someone measuring its impact on the above numbers on a staging theme first.
The second rule is the one that matters. Most app bloat happens because a marketing manager installs something on a Friday and nobody notices until the Core Web Vitals report turns red two months later.
Common traps we see
Trusting "Shopify-certified" as a performance signal. It isn't. Certification covers API behaviour and security, not script weight.
Deferring everything. Some apps genuinely need to run early — reviews that render structured data, geolocation that affects shown prices, A/B testing frameworks that must execute before paint to avoid flicker. Deferring these breaks them.
Removing an app without checking what calls it. Metafields, custom sections, and automation flows sometimes depend on an app's data model. Export first, then uninstall.
Measuring on your MacBook on fibre. Always test on a throttled mid-range mobile profile. Your customers are not on your network.
Where we'd start
If you only have an afternoon: pull the ScriptTag list via the Admin API, grep your theme for hardcoded injections, and kill the orphans. That alone usually recovers 300 – 600 KB and takes under two hours.
If you have a week: run the full four-step audit, build the scorecard, and have the keep-replace-defer-kill conversation with whoever owns the P&L. The decisions are easier when the cost of each app is sitting in a spreadsheet in front of them.
And if you want someone to run this audit alongside your team, that's exactly the kind of work our e-commerce engineering practice exists for.
Want a team like ours?
72Technologies builds production software for the kind of teams who actually read this blog.
Start a projectKeep reading
Shopify Section Rendering API: The Underused Trick That Fixed Our Collection Page Jank
A field report on using Shopify's Section Rendering API to kill full-page reloads on filtered collection pages, with the gotchas we hit around caching, analytics, and infinite scroll.
Shopify Product Page LCP Under 2 Seconds: A Core Web Vitals Playbook
Most Shopify product pages ship with an LCP between 3 and 5 seconds. Here's the engineering checklist we run through to drag that number under 2, without ripping out the theme or going headless.

WhatsApp Commerce Checkout in LATAM: What Actually Converts
A field report on building WhatsApp-first checkouts for stores in Mexico, Colombia and Brazil — what breaks, what converts, and the stack choices we'd repeat.
