Shopify ParserBlockingJavaScript — defer vs async vs type=module: The Definitive Guide
Every non-deferred script tag in your Shopify theme is a roadblock. When the browser's HTML parser encounters a <script> tag without defer or async, it stops parsing HTML, fetches the script, executes it, then resumes parsing. During that pause — which can be 100-500ms per script on a slow connection — nothing else renders. Shopify Theme Check flags this as a ParserBlockingJavaScript error. This guide explains exactly what the three options (defer, async, type=module) do and which to use for every script in your theme.
What happens with a parser-blocking script
The browser parses HTML top to bottom. When it hits <script src="app.js"></script>, it: stops the HTML parser, sends a network request for app.js, waits for the response, executes the JavaScript, then resumes HTML parsing. For a script that takes 200ms to download and 50ms to execute, that is 250ms where nothing on the page renders. On a Shopify theme with three parser-blocking scripts, that can be 750ms+ of blocked rendering before a single pixel is painted.
defer — the right choice for most theme JavaScript
defer tells the browser to fetch the script in parallel with HTML parsing, then execute it after HTML parsing is complete but before DOMContentLoaded fires. Scripts with defer execute in the order they appear in the HTML. Use defer for any JavaScript that needs a complete DOM — event listeners, component initialization, cart logic, animations. This is the correct choice for the vast majority of custom theme JavaScript.
async — only for fully independent scripts
async tells the browser to fetch the script in parallel and execute it as soon as it downloads, interrupting HTML parsing if necessary. Scripts with async execute in download-completion order, not document order. Use async only for scripts that have no dependencies on other scripts and no dependencies on the DOM — analytics tags like Google Analytics, A/B testing tools that must execute early. Never use async for scripts that initialize components or rely on other JavaScript.
type=module — defer behavior by default
Scripts with type=module are deferred by default — they behave like defer without the attribute. Additionally, modules have strict mode automatically, import/export support, and are executed once regardless of how many times they are included. Use type=module for modern ES module JavaScript. Note: the Shopify script_tag Liquid filter does not add defer or async — you must either use an explicit <script defer src=...> tag or move script loading to JavaScript section files.
The fix: before and after
// CODE_COMPARISON
Frequently asked questions
- Why doesn't the Shopify script_tag filter add defer automatically?
The script_tag Liquid filter generates a basic <script src="..."></script> tag without any loading attributes. Shopify has not updated it to add defer by default, possibly for backward compatibility. Always use an explicit <script defer src="{{ 'file.js' | asset_url }}"> tag instead of script_tag for any JavaScript that can be deferred — which is nearly all theme JavaScript.
- Can I put defer on every script in my theme?
Nearly. defer is safe for any JavaScript that: does not use document.write(), does not need to execute before HTML parsing completes, and does not have dependencies on inline scripts that execute before it. The only scripts that cannot be deferred are: A/B testing tools that need to modify the DOM before rendering (use async instead), and Liquid-generated inline scripts that provide data to downstream deferred scripts (leave these inline or move data to data attributes).
- What does the Shopify ParserBlockingJavaScript Theme Check rule actually check?
The ParserBlockingJavaScript rule in Theme Check flags any <script src="..."> tag in Liquid files that does not have defer, async, or type=module. It does not check inline scripts (which are inherently blocking but often unavoidable for configuration data). Fix by adding defer to flagged script tags. Run: npx @shopify/theme-check-cli . to see all violations.
// SCAN_YOUR_CODE
Does your theme have this bug?
Paste your code. Syphio automatically detects and fixes this error and hundreds of others — in seconds.
Audit my theme →