Is it the theme, the apps or the third-party scripts?
On a slow Shopify store, the cause usually sits in one of three layers you control. They are the theme, the apps, and third-party scripts such as tags and pixels. A fourth layer, the Shopify platform, is shared by every store and mostly out of your hands. You find the guilty layer by measuring the same page twice: once as it is, and once with the outside scripts blocked.
We did exactly that on four public storefronts on November 11, 2026. Two were Shopify’s Dawn and Horizon theme demo stores, and two were live stores running themes built on Dawn. On the store with the most outside scripts, blocking them cut lab Total Blocking Time from 6,291 ms to 1,129 ms. The theme demos, with no outside scripts at all, still showed a 4-second and a 9-second LCP. So both layers matter, and the measurement tells you which one to fix first.
If you would rather have someone run this diagnosis for you, our website speed optimization service covers Shopify stores. Theme rebuilds sit with our Shopify development team.
What can a store owner control on Shopify, and what not?
Shopify runs the servers, so a big part of speed is out of your hands. Its developer docs describe what the platform does on every store:
- Pages are rendered by Shopify’s storefront renderer and served through a CDN backed by Cloudflare.
- Requests use HTTP/3 and TLS 1.3.
- Early Hints tell the browser about render-blocking files before the HTML arrives, with no theme change needed.
- On most pages, Shopify streams the HTML in two parts. Everything above
{{ content_for_header }}reaches the browser first.
Shopify also injects its own scripts through content_for_header. On the Dawn demo store we saw the web pixels manager, a performance kit script and prefetches of checkout files. You cannot remove those from a theme, so they belong in the “platform” column of any diagnosis.
What you do control falls into three groups:
| Layer | What it includes | Typical symptom | Who fixes it |
|---|---|---|---|
| Theme | Liquid templates, theme CSS and JavaScript, fonts, image sizes, prefetching | Slow server time, late LCP image, heavy theme JavaScript | Theme developer |
| Apps | App embeds and app blocks served from cdn.shopify.com/extensions/, plus app scripts on the app’s own domain |
Widgets that shift the layout, long main-thread tasks | Store owner (remove or swap) or app vendor |
| Third-party scripts | Tag managers, ad pixels, analytics, chat, reviews, consent banners | High blocking time, many requests to outside domains | Marketing owner, with a developer for loading order |
| Platform | Storefront renderer, CDN, checkout prefetch, web pixels manager | Shared by every store | Shopify |
The four layers behind a slow Shopify page
Shopify sets one public bar for themes. A theme in the Theme Store needs an average Lighthouse performance score of at least 60 across the home, product and collection pages. That bar covers the theme with demo content, not your store with your apps.
How did we measure four public storefronts?
This is lab data, not field data. It shows what a simulated mid-range phone on a slow 4G connection sees on one homepage load. It does not show what real shoppers experienced. Here is the method, so you can repeat it:
- Tool: Lighthouse 13.6.0 from the command line, Chrome 157 in headless mode, on a Windows 10 desktop.
- Settings: performance category only, mobile form factor and screen emulation, Lighthouse’s default simulated throttling.
- Runs: 3 runs per page and setting, reporting the median. All runs took place on November 11, 2026, between 02:57 and 03:18 UTC.
- Pages: the homepage of each store, named here only by URL.
- Blocking: for the live stores, we collected every outside domain from the three baseline runs. We added the app files Shopify serves from
cdn.shopify.com/extensions/, then ran three more times with all of them passed to--blocked-url-patterns. - Sorting requests: a small script put each request into one bucket. The buckets were theme files, media, app extensions, third-party domains and Shopify platform files, such as the web pixels manager and checkout prefetches.
npx lighthouse https://www.deathwishcoffee.com/ --preset=perf --form-factor=mobile \
--screenEmulation.mobile --output=json --output-path=store-a-blocked-1.json \
--chrome-flags="--headless=new" --quiet \
--blocked-url-patterns="*://www.googletagmanager.com/*" \
--blocked-url-patterns="*://static.klaviyo.com/*" \
--blocked-url-patterns="*cdn.shopify.com/extensions/*" # ...77 patterns in totalThe four stores were theme-dawn-demo.myshopify.com (Dawn 15.0.0), theme-horizon-demo.myshopify.com (Horizon 4.4.0), www.deathwishcoffee.com and www.brooklinen.com. We read the theme names from each page’s Shopify.theme object. We have no relationship with any of these stores, and a homepage test says nothing about how they convert.
What did the two Shopify demo themes show?
The demo stores run a stock theme with no apps and no outside tags. They show what the theme and the platform cost on their own.
| Median of 3 runs | Dawn demo | Horizon demo |
|---|---|---|
| Performance score | 72 | 61 |
| First Contentful Paint | 1.79 s | 0.92 s |
| Largest Contentful Paint | 4.00 s | 9.11 s |
| Total Blocking Time | 503 ms (runs: 452 to 1,379) | 370 ms (runs: 366 to 386) |
| Cumulative Layout Shift | 0.007 | 0 |
| Requests | 136 | 242 |
| Total transfer | 1,277 KB | 2,986 KB |
| Theme files (CSS, JS, fonts) | 77 KB | 321 KB |
| Images and media | 368 KB | 956 KB |
| Prefetched pages | none | 583 KB (10 pages) |
| Shopify platform files, including checkout prefetch and web pixels | 832 KB | 1,127 KB |
Lighthouse 13.6.0, mobile, simulated throttling, November 11, 2026
Three things stand out:
- Platform files outweigh the theme. On the Dawn demo, Shopify’s own files came to 832 KB, about 10 times the theme’s 77 KB. About 508 KB of that was checkout code prefetched during the homepage load. No theme setting removes it.
- Horizon’s slow LCP is a bandwidth problem, not a big image. The hero image is an 82 KB PNG. In Lighthouse’s breakdown, 8.1 of the 9.1 seconds was “resource load duration”. On a simulated slow connection, that image shares bandwidth with about 2.9 MB of other downloads, including 583 KB of prefetched product and collection pages. From the Lighthouse trace we could not tell whether the theme or a platform script started those prefetches.
- Single runs mislead. Dawn’s blocking time ranged from 452 to 1,379 ms across three identical runs. As a control, we ran Horizon three more times with only one Shopify asset host blocked. Its median TBT moved from 370 to 381 ms, while single runs spread from 293 to 596 ms. Treat differences smaller than that spread as noise.
What did third-party scripts cost on a live store?
The first live store, www.deathwishcoffee.com, loaded files from 63 outside domains on its homepage. They included tag managers, ad and analytics pixels, email and SMS tools, reviews, subscriptions, loyalty and fraud tools. Blocking them gave the clearest result in our test:
Comparison
| Median of 3 runs | As loaded | Outside scripts and app files blocked | Change |
|---|---|---|---|
| Performance score | 36 | 47 | +11 |
| Largest Contentful Paint | 6.88 s | 5.32 s | -1.56 s |
| Total Blocking Time | 6,291 ms | 1,129 ms | -82% |
| Main-thread work | 18.6 s | 7.1 s | -11.5 s |
| Requests | 502 | 357 | -145 |
| Total transfer | 7,357 KB | 3,706 KB | -3,651 KB |
| JavaScript transfer | 5,225 KB | 2,063 KB | -3,162 KB |
| Third-party transfer | 4,951 KB | 1,371 KB | -3,580 KB |
One caution applies to all six runs on this store. Each carried Lighthouse’s warning that the page loaded too slowly to finish within the time limit. Lighthouse still returned every metric, but scripts that load very late may be undercounted, in both columns.
Blocking time is the number to watch here. Lighthouse cannot measure Interaction to Next Paint on a page load, but long main-thread tasks are what make taps feel slow. Removing the outside scripts took away more than 11 seconds of simulated main-thread work.
The theme was not off the hook, though. With every outside script blocked, LCP was still 5.3 seconds and the score only reached 47. So this store has two jobs: trim the tags, then work on the theme and its images.
What did blocking miss?
About 1,371 KB of third-party traffic survived the block. Every byte of it came from inside iframes, not from the page. The store loaded 14 custom web pixel sandboxes, which are iframes Shopify creates at /web-pixels@.../custom/.... The tags inside them kept loading Google Tag Manager, TikTok, Microsoft Clarity and LinkedIn scripts.
Lighthouse’s URL blocking did not reach requests made inside those sandboxes in our runs. So the “blocked” column understates what the outside scripts cost. If you use custom pixels in Shopify’s Customer events settings, test with those pixels switched off as well. Blocking domains alone will not cover them.
What if Lighthouse cannot finish the test?
The second live store, www.brooklinen.com, went one step further. All six runs carried the same time-limit warning, and here Lighthouse could not compute a score at all. Lighthouse waits for the network to go quiet, and this page never did. The runs logged about 700 requests each. The first one included 169 calls to the store’s own Storefront API, 43 review-widget calls and 6 video files. Without a quiet period, Lighthouse returns no performance score and no Total Blocking Time.
The paint metrics still came through. Median First Contentful Paint went from 4.22 s to 1.92 s with the outside scripts blocked, and LCP from 11.76 s to 8.42 s. Third-party transfer on the page dropped from 1,827 KB to 273 KB. Treat these as rough numbers: each run stopped at a different point, so the byte totals are not comparable.
If this happens on your store, the warning is itself the finding. Look for polling, autoplay video and widgets that keep fetching data after the page has loaded.
How do you diagnose your own Shopify store, step by step?
You do not need our scripts to repeat this. Chrome DevTools, PageSpeed Insights and Shopify’s own tools cover every step.
Steps
-
Start with field data
Open PageSpeed Insights for your homepage and a product page. The top panel shows Chrome UX Report data: the 75th percentile of real Chrome visits over 28 days. If it says there is not enough data, use the Web Performance dashboard in your Shopify admin, which reports real visits for your store only.
-
Find the slow metric
A poor LCP points at images, fonts, render-blocking files or slow server time. A poor INP points at JavaScript on the main thread. A poor CLS points at late-loading widgets, banners and images without dimensions.
-
Run Lighthouse three times
Use mobile, an incognito window and no browser extensions. Take the median of three runs, not the fastest one. Single runs swing by hundreds of milliseconds, as our demo-store repeats show.
-
Read the third-party table
In the Lighthouse report, open the third-party insight. It lists each outside company with its transfer size and main-thread time. Sort it by main-thread time; that column points at likely INP trouble.
-
Block the outside scripts and run again
In DevTools, open the Network request blocking panel and block the third-party domains from that table. Or pass them to the Lighthouse CLI with
--blocked-url-patterns. The difference between the two medians is what those scripts cost. Scripts inside custom pixel sandboxes may escape the block, so switch those pixels off in a test as well. -
Test the theme on its own
Duplicate your live theme, turn off the app embeds in the copy, and test the preview link. Install Shopify Theme Inspector for Chrome to see which Liquid sections take the most server time. Remember that preview pages are not streamed, so compare previews with previews, not with the live store.
-
Switch apps back on one at a time
Re-enable app embeds one by one in the duplicate, testing after each. The app that moves the median is your suspect. Check the theme code for snippets that uninstalled apps left behind.
# Baseline, then the same page with the outside domains blocked (repeat each 3 times)
npx lighthouse https://your-store.example/ --preset=perf --form-factor=mobile \
--screenEmulation.mobile --output=json --output-path=base-1.json
npx lighthouse https://your-store.example/ --preset=perf --form-factor=mobile \
--screenEmulation.mobile --output=json --output-path=blocked-1.json \
--blocked-url-patterns="*://static.klaviyo.com/*" \
--blocked-url-patterns="*://www.googletagmanager.com/*"Which fixes belong to which layer?
Once you know the layer, the fix list is short. These are the changes we would test first, in this order:
| Finding | First fix to test |
|---|---|
| High blocking time that drops sharply when outside scripts are blocked | Remove tags nobody uses, move the rest into a tag manager with consent rules, and delay non-essential tags until after load or first interaction |
| Many pixels in custom pixel sandboxes | Review Customer events; keep one pixel per platform and drop duplicates sent through both a pixel and a tag manager |
| App widgets that shift the layout or block the main thread | Turn off the app embed in a duplicate theme and measure; ask the vendor for a lighter loading option, or replace the app |
| Slow LCP with outside scripts blocked | Load the LCP image eagerly with fetchpriority="high", size it with image_url, cut competing downloads such as autoplay video and page prefetches |
| Slow server time on cache misses | Profile Liquid with Theme Inspector; remove nested loops and repeated metafield reads |
| A page that never goes quiet | Find polling and repeated API calls; load reviews and recommendations on scroll |
First fixes by layer, from our measurements and Shopify’s theme performance guidance
Change one thing at a time and re-run the three-run median. Then wait 28 days and check the Chrome UX Report or the Web Performance dashboard, because real visitors are the result that counts. If the fix list keeps growing after a migration, the steps in our website migration checklist help you measure before and after a platform move.
Limits: what this guide does not cover
- Lab data only. We tested one homepage load per run with simulated throttling. We did not pull field data for these stores, and lab numbers do not predict their real Core Web Vitals.
- Homepages only. Product, collection and cart pages load different apps and were not tested.
- Four stores, three runs each. That is enough to show the method and large effects, not to rank themes or stores. Differences smaller than the run-to-run spread are noise.
- No store access. We have no admin access to any of these stores, so we did not disable apps, change pixels or run Theme Inspector on them. The diagnosis steps for your own store come from Shopify’s documentation.
- Blocking is not removal. Blocking a script can break features that depend on it, and it missed scripts inside custom pixel sandboxes. The real cost of an app is only known after you remove it in a test theme.
- Visitor region. Some stores load different tags by region and consent status. Shoppers elsewhere may get a different set of scripts.