LUCENTCOMMERCEGET A FREE STORE AUDITFREE AUDIT

PERFORMANCE · CORE WEB VITALS · THEME · 14 MAY 2026 · 7 MIN READ

Loading strategy: what to defer, what to inline, what to drop

Every speed project is the same three decisions applied to every file the page asks for. Most of them start with the wrong one.

A loading waterfall with the blocking script highlighted

You improve loading performance on Shopify by making one of three decisions about every resource the page requests, in this order: drop it, defer it, or inline it. Dropping is first because a file that is not requested cannot be optimised into something faster, and on a typical mid-market store the largest single win is removing apps and scripts nobody can name an owner for. Deferring is second and covers almost all JavaScript. Inlining is last, applies to a small amount of CSS and one or two genuinely critical resources, and is where most speed projects wrongly begin — it is the fiddliest work and the one most easily undone by the next theme update.

IN SHORT

  • Work in the order drop, defer, inline: deletion is permanent, deferral is cheap, and inlining is fragile work that a theme update can undo.
  • Shopify documents `defer` as the default for non-critical scripts and `async` only for independent third-party scripts where execution order does not matter.
  • The LCP image must never be lazy-loaded, and Shopify recommends `fetchpriority="high"` on it so the browser prioritises it before layout completes.
  • Shopify advises preloading only one or two critical resources the browser discovers late, because overuse competes with the browser’s own prioritisation.
  • Base stylesheets, font preloads and the import map belong above `{{ content_for_header }}` so they download while sections are still rendering.
  • Each additional stylesheet `<link>` triggers an expensive style recalculation, so consolidating them beats minifying any one of them.
  • A resource with no named owner is a resource to drop — and the audit that finds those is a spreadsheet, not a profiler.

Three decisions, and the order matters more than any of them

Loading performance is not a technique. It is a triage applied to every file a page requests, and there are only three possible verdicts.

Drop it. The resource does not need to exist. Nothing downloads, nothing parses, nothing executes, and no future theme update can reintroduce the cost.

Defer it. The resource is needed, but not before the page is usable. It downloads at a lower priority, or after parsing, or only when somebody interacts with the thing that needs it.

Inline it. The resource is needed immediately and the round trip to fetch it is itself the delay, so it ships inside the HTML.

Teams almost always start with the third one, because it feels like engineering. Extracting critical CSS, hand-tuning a preload list and rewriting a bundling strategy are satisfying work with visible artefacts. They are also the most fragile results you can produce: the inlined CSS drifts out of step with the stylesheet it was extracted from, and the next theme release quietly puts the cost back. Meanwhile the store is still loading four scripts from an app that was uninstalled in March.

Do the cheap permanent thing first.

Drop: the inventory beats the profiler

The tool for this stage is a spreadsheet, not a waterfall. List every third-party request the page makes and write a name next to each one — a person, not a department, who will say what it does and what happens if it stops. Shopify’s own guidance is blunt about this: audit all scripts, including apps, tracking pixels and theme code, and remove anything that is not earning its performance cost.

What the list reliably turns up:

  • Orphaned app code. An app uninstalled months ago left a snippet, a script tag or an asset reference in the theme. Uninstalling removes the app; it does not always remove what the app wrote into your code.
  • Duplicate analytics. The same platform loaded twice — once through a tag manager, once hardcoded into the theme by whoever was in a hurry. Both fire. Your numbers are fine and your page is paying twice.
  • Anti-flicker snippets with no tests behind them. A/B testing tools ship a snippet that hides the page until the tool responds. Shopify explicitly recommends disabling it when no tests are running. A store not currently testing anything is paying a blank-screen delay for nothing.
  • Sitewide scripts serving one template. A reviews widget, a size guide or an upsell tool loaded on every page to appear on product pages. This is the most common one and the most easily fixed without removing the app at all.
  • Fonts nobody uses. Four weights loaded, two rendered. And if any of them come from a third-party font host, Shopify recommends self-hosting on its own CDN instead, which removes a whole connection from the critical path.

Two ways to drop something without losing it

Deletion is easy to agree to in principle and hard in practice, because everything on the list belongs to somebody. Two moves get most of the benefit without an argument.

Conditional loading. A script that serves one template should load on that template. Shopify’s theme guidance points at section.index and section.location for exactly this — differentiating above-the-fold and below-the-fold behaviour — and the same thinking applies at template level. The reviews widget stays; it stops loading on the homepage.

Move tracking out of the theme. Marketing tags added directly to Liquid execute in your page with full access to it. Shopify’s Web Pixels API runs pixels inside a documented sandbox instead — a strict sandbox for app pixels and a lax one for custom pixels — with a controlled set of browser APIs and customer events rather than free run of your DOM. Moving a tag there is not primarily a speed change and should not be sold as one, but it does take the code out of your theme, which means it stops being something your next theme rebuild has to carry.

Defer: almost all JavaScript, and Shopify says which flag

Once the list is shorter, the rest is scheduling. Shopify’s documented position is specific and worth following rather than reasoning from first principles.

Use defer as the default for non-critical scripts. Use async only for independent third-party scripts where execution order does not matter — which is a narrower set than it sounds, because any two scripts that touch the same thing have an order even when nobody wrote it down.

Better than deferring is not loading at all until somebody needs it. Shopify recommends dynamic import() inside event listeners so a module loads only when a user actually interacts with the component it powers. A size guide modal, a store locator, a video player, a bundle builder — none of these need to be in the initial download, and moving them behind the interaction that reveals them is usually a larger win than any amount of minification.

On bundling, the current guidance may surprise anyone who last looked a few years ago: load JavaScript modules with import maps instead of bundlers, and Shopify automatically includes es-module-shims for browsers that need it. That is a real simplification — no build step between your source and your theme — and it is a reason to be sceptical of a proposal that adds a bundler to a Shopify theme without saying what it buys.

One placement rule sits underneath all of this. Scripts may go above {{ content_for_header }} only if they do not read Shopify.*, because those globals are not defined yet. Something that looks like an intermittent JavaScript bug in production is quite often this.

Inline and preload: a short list, deliberately

This is where restraint pays. Preload is a priority signal, and a signal everything carries is not a signal — Shopify recommends <link rel="preload"> for only one or two critical resources the browser discovers late, and notes that overuse competes with the browser’s own prioritisation. A theme with fourteen preloads in the head has told the browser nothing.

The things worth the budget:

  • The LCP image. Never lazy-load it. Shopify’s guidance is to load images in the initial viewport eagerly and apply fetchpriority="high" to the LCP image so its importance is signalled before layout completes. Use an <img> rather than a CSS background-image for hero content, so the browser can find it in the markup.
  • Critical CSS. Load the CSS for content visible in the initial viewport synchronously, and reserve the async CSS pattern for below-the-fold sections. Note the framing: this is about *how* the initial stylesheet loads, not about extracting a bespoke critical block that will rot.
  • Font preloads, above `{{ content_for_header }}`. Shopify recommends placing base stylesheets, font preloads and the import map above that tag so they download while sections are still rendering server-side. It is a one-line move with no downside.
  • Connections you know you will need. preconnect establishes DNS, TCP and TLS to critical third-party domains early. Worth it for the one or two you could not drop; not worth it for the eight you should have.

The counting problem nobody mentions

One detail from Shopify’s guidance reframes a lot of CSS work: minimise the number of separate <link> tags for stylesheets, because each additional stylesheet triggers an expensive style recalculation.

That is a cost per file, not per kilobyte. A theme where every section ships its own stylesheet — a pattern that looks like good hygiene and often is, for maintenance — can be paying more in recalculation than it would in bytes from one consolidated file. It is worth measuring on your own store rather than assuming either way, but if your stylesheet count is in the double digits, that is the first thing to look at and it will not be visible in a byte-size report.

The same applies to layout stability. Always include width and height on images, or use image_tag, which adds them automatically. Animate with transform and opacity rather than layout properties. And remove CSS animations from the LCP image specifically — Shopify notes they delay the LCP event even after the image has loaded, which is an unusually easy thing to get wrong because the image genuinely is there.

What we would argue against

Three proposals we have talked clients out of, all of which sound like performance work.

A reverse proxy in front of the storefront. Shopify is explicit: do not do this. Proxies mask real problems, distort metrics and add latency. If the argument for one is caching, Shopify already caches; if it is edge logic, be very sure the logic is worth the layer.

A headless rebuild to fix loading speed. The scripts that make the store slow are, in the main, the ones that will be reimplemented in the new front end, because the business still wants reviews and personalisation and analytics. Do the triage first. If the store is fast afterwards you have saved a rebuild, and if it is not you will start the rebuild knowing what it actually has to carry.

A score target with no page behind it. Optimising a number until it goes up produces work that is hard to defend later. Pick the page that makes money, measure the metric the buyer experiences on it, and make that better. A homepage scoring well while the product template loads eleven scripts is not a performance result.

The order, once more

Drop what nobody owns. Defer everything that is not needed before first paint, and load on interaction whatever is not needed until somebody clicks. Inline and preload a short, deliberate list. Then measure — on a mid-range phone on a real connection, not on the machine that built the theme.

The reason to keep this order is not purity. It is that each stage makes the next one smaller. Tuning the loading strategy of scripts you are about to delete is the most common way a speed project spends a month to arrive where it started.

Questions this raises

How do you improve loading performance on a Shopify store?

Triage every resource the page requests in three stages. First drop what is not earning its cost — orphaned app code, duplicate analytics, anti-flicker snippets with no tests behind them, sitewide scripts serving one template. Then defer the rest of the JavaScript, loading modules on interaction where possible. Only then inline critical CSS and preload one or two genuinely critical resources. Doing it in that order means each stage has less to work on.

Should scripts use defer or async?

Shopify documents `defer` as the default for non-critical scripts, and `async` only for independent third-party scripts where execution order does not matter. That second set is smaller than it looks, because two scripts touching the same thing have an order whether or not anyone wrote it down. Better than either is a dynamic `import()` inside an event listener, so the module loads only when someone uses the component.

How many resources should I preload?

Shopify recommends `<link rel="preload">` for only one or two critical resources that the browser discovers late, and warns that overuse competes with the browser’s own prioritisation. A head full of preloads has communicated nothing. Spend the budget on the LCP image and fonts, and use `preconnect` for the third-party domains you could not remove.

Should the hero image be lazy-loaded?

No. Shopify’s guidance is that the LCP image must never be lazy-loaded — load images in the initial viewport eagerly and apply `fetchpriority="high"` to the LCP one. Use an `<img>` rather than a CSS background image so the browser can discover it in the markup, and remove CSS animations from it, because they delay the LCP event even once the image has loaded.

Is it better to have one stylesheet or one per section?

Shopify notes that each additional stylesheet `<link>` triggers an expensive style recalculation, so the cost is per file rather than per kilobyte. A theme with a stylesheet per section can pay more in recalculation than it saves in bytes. If your link count is in double figures, consolidation is likely to beat any amount of minification — measure it on your own store before committing either way.

Will going headless make my store load faster?

Not by itself, and often not at all. Most of the weight on a slow Shopify store comes from third-party scripts that the business still wants after the rebuild, so they get reimplemented in the new front end. Run the triage on the theme first. If the store ends up fast, you have avoided a rebuild; if it does not, you will begin one knowing exactly what it has to carry.

NEXT STEP

Free store audit

A senior Shopify engineer reviews your storefront, theme performance and checkout, then sends a prioritised list of fixes.