How to Fix Render-Blocking Scripts in Shopify (2026)

Last updated
•
Expert reviewed
•
5 min read

Key takeaways

  • Render-blocking scripts stop the browser from showing your page until they load.
  • Fix: add defer or async to non-critical <script> tags in theme.liquid.
  • Shopify’s {% javascript %} tag in sections bundles and defers scripts automatically.
  • PageSpeed Insights lists the scripts and stylesheets that block rendering.

To fix render-blocking scripts in Shopify, add defer (theme scripts) or async (independent analytics scripts) to the <script> tags in theme.liquid’s <head>, and remove app scripts you no longer use.

When a browser encounters a <script> tag in your HTML, it stops, downloads the script, executes it, then continues building the page. If that script is in your <head>, your entire page is frozen while the script downloads. On a slow mobile connection, this pause is visible — a white screen where your store should be.

This is called “parser blocking” or “render blocking,” and it’s one of the most impactful performance problems to fix in Shopify.

What causes render-blocking scripts in Shopify

Script tags in <head> without async or defer. The most common cause. Scripts added by apps or customizations often include a plain <script src="..."> without either attribute.

Large theme JavaScript files loaded synchronously. Some older themes load all their JavaScript in <head> blocking paint.

Third-party analytics and marketing scripts. Google Tag Manager, Facebook Pixel, and similar scripts are frequently placed in <head> because their documentation says to — but they should still use async.


The difference between async and defer

Both prevent render blocking, but they behave differently:

async: The script downloads in parallel while the HTML parses. It executes as soon as it finishes downloading — potentially interrupting HTML parsing. Use for independent scripts that don’t depend on the DOM being ready (analytics, pixels).

defer: The script downloads in parallel while the HTML parses. It executes after the HTML is fully parsed, in document order. Use for scripts that interact with the DOM (theme features, product functionality).

No attribute (default): The script blocks HTML parsing entirely while it downloads and executes. Avoid this for any non-critical script in <head>.


How to add defer or async to scripts in Shopify

Step 1 - Duplicate your theme.

Step 2 - Open theme.liquid in the code editor.

Step 3 - Find <script> tags in the <head> section. They look like:

<script src="https://example.com/script.js"></script>

Step 4 - Add the appropriate attribute:

<!-- For analytics/pixel scripts: -->
<script src="https://analytics.example.com/script.js" async></script>

<!-- For theme functionality scripts: -->
<script src="{{ 'theme.js' | asset_url }}" defer></script>

Important: Don’t blindly add async to all scripts. If Script B depends on Script A, making Script A async can cause Script B to fail if Script B loads first.


Using Shopify’s {% javascript %} blocks in sections

The cleanest solution for theme functionality: put JavaScript inside section files using {% javascript %} tags rather than in global theme.liquid scripts.

{% javascript %}
  // This JavaScript is:
  // 1. Automatically bundled with other section JS
  // 2. Deferred automatically by Shopify
  document.querySelector('.my-section').addEventListener('click', function() {
    // section-specific behavior
  });
{% endjavascript %}

Shopify compiles all section {% javascript %} blocks into a single optimized bundle (shopify-section-*.js) that loads deferred. It keeps section code self-contained.


Fixing GTM render-blocking

Google Tag Manager’s install snippet places a <script> tag in <head>. The standard install looks like:

<script>
    (function(w,d,s,l,i){...})(window,document,'script','dataLayer','GTM-XXXXX');
</script>

This is already an inline script (no src) so async/defer don’t apply in the same way. The GTM snippet is designed to be small and fast — the real performance issue is the tags inside your GTM container.

In GTM, set all tags to fire on “Window Loaded” or “DOM Ready” rather than “Page View” unless they genuinely need to fire before the page renders. This moves tag execution to after the page is visible.

For the rest of the speed checklist, see how to speed up a Shopify theme.


Identifying render-blocking scripts

PageSpeed Insights: Run your store URL at pagespeed.web.dev. PageSpeed Insights flags these as render-blocking requests and lists each script and stylesheet by URL.

Chrome DevTools — Performance tab: Record a page load. In the flame chart, look for Parse HTML events being interrupted by Evaluate Script events at the start of the timeline.

Shopify Theme Inspector: Shows which Liquid templates are slowest to render. For script-related blocking, use PageSpeed Insights instead.

Build fast Shopify pages with clean, deferred code with Fudge.
Start free trial

What about render-blocking CSS?

CSS is inherently render-blocking — the browser needs CSS before it can paint anything. But you can minimize the impact:

Critical CSS inline: Move the CSS needed to render above-the-fold content inline into <head>. Load the rest of your CSS asynchronously using <link rel="preload"> with onload.

Remove unused CSS: Fewer CSS rules = faster parse time.

Avoid CSS @import in stylesheets: These create cascading blocking requests.

For most Shopify stores, the gains from CSS optimization are smaller than fixing JavaScript blocking. Focus on JavaScript first.


FAQ

Should I add async or defer to the Google Tag Manager script?

GTM's standard install is an inline script (no src) — async/defer don't apply directly. The GTM bootstrap snippet is small and fast. The real performance issue is the tags inside your GTM container. Set tag triggers to "Window Loaded" or "DOM Ready" rather than "Page View" so they fire after critical render, not before.

Why doesn't deferring scripts improve my PageSpeed score?

Three common reasons: (1) the script you deferred wasn't actually render-blocking (already async, or below the fold), (2) other render-blocking resources (CSS, fonts) are still the bottleneck, or (3) Total Blocking Time (TBT) is dominated by the script's execution, not just download — defer doesn't fix slow execution, only blocking download.

Can I move scripts to the bottom of the page instead of using defer?

Yes — placing scripts before </body> historically achieved similar non-blocking behavior. Modern best practice prefers defer because it preserves execution order and works regardless of where the tag is placed in the HTML. Both work; defer is cleaner for maintainability.

Will deferring scripts break apps that depend on jQuery or other libraries?

Possibly. Scripts that depend on a library being already loaded will fail if the library hasn't finished executing. Solution: defer scripts in dependency order. Scripts with defer execute in document order — put the library tag first. Scripts with async can execute in any order; check dependencies before adding async.

How do I find render-blocking scripts I didn't add myself?

PageSpeed Insights flags these as render-blocking requests and lists each one by URL. Scripts you didn't add usually come from installed apps. Check Settings → Apps to identify which app loads each blocking script — and disable apps you no longer use. App removals are the highest-leverage performance fix. To keep the feature without the app's script weight, describe what the app does to Fudge, the AI operator for Shopify stores, and it builds a native version into your theme that loads only where it's used. Slow pages are also one of the things Fudge checks when it looks for where shoppers drop off.

Jacques's signature
Build fast Shopify pages with properly structured code by describing what you want.

You might also be interested in

Jacques Blom
Jacques Blom
CTO at Fudge.