The average Shopify store runs between 6 and 15 apps. Each one promises to increase conversions, improve customer experience, or automate operations. What most store owners do not see is what those apps cost in page performance — the JavaScript files loaded on every page, the render-blocking stylesheets injected into the head, the API calls firing on every interaction, and the DOM elements inserted that cause layout shift.
We have audited hundreds of Shopify stores through our Shopify development work, and the pattern is remarkably consistent: 40-70% of the total JavaScript loaded on a typical Shopify storefront comes from third-party apps. That is not inherently bad — some of those apps drive significant revenue. The problem is that most store owners have never measured the cost, so they cannot make informed decisions about the trade-off.
This guide provides a technical methodology for auditing every app on your Shopify store, measuring its performance impact in concrete terms, and making data-driven decisions about your app stack. This builds on the broader app stack audit framework we use with clients.
Why app performance audits matter
Every kilobyte of JavaScript your store loads has a measurable cost. The browser must download, parse, compile, and execute it. On mobile devices — which typically account for 65-75% of ecommerce traffic in the UK — this cost is amplified because mobile processors are significantly slower than desktop CPUs and mobile networks have higher latency.
The performance impact of apps compounds. One app adding 50KB of JavaScript is barely noticeable. Ten apps each adding 50KB is half a megabyte of scripts competing for main thread time, network bandwidth, and browser memory. The relationship between total JavaScript and Core Web Vitals scores is well documented.
There is also a commercial dimension. Apps have monthly costs. If an app costs £49/month but adds 300ms to your page load time and reduces your conversion rate by 0.3%, the true cost might be £49 in subscription fees plus £2,000 in lost revenue. Without the performance data, you cannot calculate the actual ROI.
The goal of an app performance audit is not to remove apps. It is to make informed decisions about which apps deliver value that justifies their performance cost, and which ones do not.
Establishing your performance baseline
Before you measure individual apps, you need a baseline for your store’s current performance. Run these measurements on your three most important page types: homepage, a collection page, and a product page.
Lighthouse baseline
# Run Lighthouse from the command line for consistent results
npx lighthouse https://your-store.myshopify.com \
--output=json \
--output-path=./baseline-homepage.json \
--chrome-flags="--headless" \
--throttling-method=simulate \
--preset=desktop
# Run mobile too (the default)
npx lighthouse https://your-store.myshopify.com \
--output=json \
--output-path=./baseline-homepage-mobile.json \
--chrome-flags="--headless"
WebPageTest baseline
Run WebPageTest from a UK location (London) with a “Moto G Power” device profile and 4G connection. Record:
- Time to First Byte (TTFB)
- First Contentful Paint (FCP)
- Largest Contentful Paint (LCP)
- Total Blocking Time (TBT)
- Cumulative Layout Shift (CLS)
- Total page weight (HTML + CSS + JS + images + fonts)
- Total JavaScript weight (all scripts combined)
- Number of requests
Building a script inventory
The first step in the audit is cataloguing every script your store loads and attributing it to an app or your theme.
// Run in browser console on your storefront
// Lists all scripts with their source, size, and timing
(async () => {
const resources = performance.getEntriesByType('resource')
.filter(r => r.initiatorType === 'script' || r.name.endsWith('.js'));
const scripts = resources.map(r => ({
url: r.name,
domain: new URL(r.name).hostname,
transferSize: (r.transferSize / 1024).toFixed(1) + ' KB',
duration: r.duration.toFixed(0) + ' ms',
renderBlocking: r.renderBlockingStatus || 'unknown'
}));
console.table(scripts.sort((a, b) =>
parseFloat(b.transferSize) - parseFloat(a.transferSize)
));
const totalJS = resources.reduce((sum, r) => sum + r.transferSize, 0);
console.log('Total JS transferred:', (totalJS / 1024).toFixed(1), 'KB');
})();
Map each script domain to its source. Common patterns:
cdn.shopify.com— Shopify platform and your theme assetsstatic.klaviyo.com— Klaviyoconnect.facebook.net— Meta Pixelwww.googletagmanager.com— Google Tag Managerloox.io,judge.me,stamped.io— Review appsrebuyengine.com,bold.ly— Upsell/cross-sell apps
Network waterfall analysis
The network waterfall shows the timeline of every resource request during page load. For app performance auditing, you are looking for three things:
1. Render-blocking scripts
Scripts in the <head> without async or defer attributes block HTML parsing. The browser stops rendering until the script is downloaded and executed. Identify which app scripts are render-blocking by checking the waterfall for scripts that delay First Contentful Paint.
2. Script chains
Some apps load a small initial script that then loads additional scripts. This creates a dependency chain where each script must complete before the next one begins. A 3-script chain with 100ms network latency each adds 300ms to the total load time, even if the scripts themselves are small.
3. Duplicate requests
Multiple apps sometimes load the same library (jQuery, Lodash, etc.) independently. We have seen stores loading jQuery three times from different CDN domains because three different apps each bundle their own copy.
// Check for duplicate library loads
const scripts = performance.getEntriesByType('resource')
.filter(r => r.name.endsWith('.js'));
const filenames = scripts.map(r => {
const url = new URL(r.name);
return url.pathname.split('/').pop();
});
const duplicates = filenames.filter((name, index) =>
filenames.indexOf(name) !== index
);
if (duplicates.length > 0) {
console.warn('Duplicate scripts detected:', [...new Set(duplicates)]);
}
Main thread profiling
Network transfer is only half the story. Once scripts arrive at the browser, they compete for main thread time — the single thread responsible for parsing HTML, calculating styles, running JavaScript, and handling user interactions.
Using Chrome DevTools Performance panel
- Open DevTools → Performance panel
- Click the record button
- Reload the page (Ctrl+Shift+R for hard reload)
- Stop recording once the page is fully loaded
- Look at the Main thread flame chart
The flame chart shows every JavaScript function that executed during page load. Long tasks (over 50ms) are highlighted with red triangles. Click on them to see which script file they originated from.
// Programmatically detect long tasks
if ('PerformanceObserver' in window) {
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.duration > 50) {
console.warn('Long task detected:', {
duration: entry.duration.toFixed(0) + 'ms',
startTime: entry.startTime.toFixed(0) + 'ms',
attribution: entry.attribution?.map(a => ({
name: a.name,
containerType: a.containerType,
containerSrc: a.containerSrc
}))
});
}
}
});
observer.observe({ type: 'longtask', buffered: true });
}
Core Web Vitals attribution
Beyond raw script size and execution time, you need to understand how each app affects the metrics Google uses for ranking and user experience.
LCP attribution
If an app loads render-blocking scripts in the head, it delays everything — including your LCP element. Use the Performance panel to measure the gap between navigation start and LCP with and without each app.
CLS attribution
Apps that inject DOM elements after initial render (review badges, pop-ups, notification bars, social proof widgets) often cause layout shift. The Layout Shift entries in PerformanceObserver tell you exactly which elements shifted and by how much.
INP attribution
Interaction to Next Paint measures responsiveness. Apps that run heavy JavaScript on every interaction (real-time search, complex cart drawers, dynamic pricing) increase INP. Use the Long Animation Frames API to identify which scripts are blocking during interactions.
Isolation testing methodology
The most accurate way to measure an individual app’s impact is isolation testing: compare performance with the app enabled versus disabled.
Method 1: Theme code removal
- Duplicate your live theme
- In the duplicate, identify and remove the app’s Liquid snippets, script tags, and CSS links
- Run Lighthouse on the duplicate (app removed) and the original (app present)
- The difference is the app’s isolated impact
Method 2: Request blocking
In Chrome DevTools, Network panel, right-click an app’s script request and select “Block request domain”. Reload the page. This blocks all resources from that domain, simulating the app being removed.
// Automate this with Puppeteer/Playwright
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
const context = await browser.newContext();
const page = await context.newPage();
// Block specific app domain
await page.route('**/*.loox.io/**', route => route.abort());
await page.goto('https://your-store.com/products/example');
// Capture performance metrics
const metrics = await page.evaluate(() => ({
lcp: performance.getEntriesByType('largest-contentful-paint')[0]?.startTime,
cls: performance.getEntriesByType('layout-shift')
.filter(e => !e.hadRecentInput)
.reduce((sum, e) => sum + e.value, 0),
totalJS: performance.getEntriesByType('resource')
.filter(r => r.name.endsWith('.js'))
.reduce((sum, r) => sum + r.transferSize, 0) / 1024
}));
console.log('Metrics without app:', metrics);
await browser.close();
})();
The keep, replace, or remove framework
Once you have performance data for each app, apply this decision framework:
Keep
The app provides measurable business value (revenue, conversion, retention) that exceeds its performance cost. Its scripts are reasonably optimised (async/defer loaded, no render-blocking, no excessive main thread usage).
Replace
The app’s functionality is needed but a lighter alternative exists, or the same feature can be built natively in Liquid/JavaScript with significantly less overhead. Common replacement candidates include announcement bars, simple pop-ups, basic trust badges, and recently-viewed product widgets.
Remove
The app is no longer used, provides marginal value, or has a performance cost that clearly exceeds its business benefit. Also remove apps that are installed but not configured, or apps that duplicate functionality provided by another app.
Document your findings and decisions in a spreadsheet with columns for: app name, monthly cost, JS size (KB), main thread time (ms), LCP impact (ms), CLS impact, business value description, and decision (keep/replace/remove).
Implementing changes safely
Removing or replacing apps on a live store requires care. Follow this sequence:
- Backup theme — duplicate your live theme before making any changes.
- Test in development — make all changes in the duplicate theme and test thoroughly.
- Remove theme code first — before uninstalling the app, remove its Liquid snippets and script references from your theme. Some apps leave orphaned code after uninstallation.
- Uninstall the app — then uninstall from the Shopify admin.
- Verify cleanup — check that no app code remains in your theme, no lingering script tags, no orphaned sections.
- Measure performance — run the same Lighthouse and WebPageTest benchmarks you used for the baseline and compare.
Ongoing performance monitoring
An app audit is not a one-time exercise. Apps update their code regularly, sometimes adding new scripts or changing loading behaviour without notifying you. Build performance monitoring into your regular store management:
- Run Lighthouse monthly and track scores over time
- Set up Google Search Console to monitor Core Web Vitals field data
- Before installing any new app, benchmark current performance and re-benchmark after installation
- Review your app stack quarterly — remove apps no longer in use
- Track total JavaScript weight monthly — if it is trending upward, investigate
App performance auditing is one of the highest-ROI activities you can undertake for your Shopify store. The data consistently shows that optimising the app stack delivers more performance improvement per hour of effort than almost any other technical intervention. The methodology is systematic, the tools are free, and the business case writes itself when you can show the revenue impact of faster page loads.
If you need help auditing your Shopify app stack or implementing performance improvements, get in touch. Performance optimisation is a core part of our Shopify development services.