Use defer and async on non-critical scripts
Use the defer attribute on non-critical scripts to prevent them from blocking the HTML parser. Use async only for independent, third-party scripts where execution order doesn't matter.
When the browser encounters a <script src=""> tag without defer or async, it stops parsing HTML, downloads the script, executes it, and then resumes parsing. Nothing below that script tag is discovered or rendered until the script finishes.
This has two costs:
- Delayed rendering. The browser can't paint anything that depends on HTML below the blocking script. This delays FCP and LCP.
- Blocked resource discovery. The browser's preload scanner finds images, stylesheets, and other resources by scanning ahead through the HTML. A blocking script pauses that scan, so resources further down the page aren't fetched until the script completes.
Adding defer or async lets the browser continue parsing HTML while the script downloads, keeping the preload scanner working and allowing earlier paint.
Anchor to Use ,[object Object], for theme scriptsUse defer for theme scripts
defer for theme scriptsScripts with defer download in the background and execute in document order after the HTML finishes parsing, but before the DOMContentLoaded event fires. This makes defer the right choice for most theme scripts because:
- Scripts that depend on the DOM can rely on the full document being available.
- Scripts that depend on each other run in the order that they appear in the HTML.
- The browser parses the entire document without interruption.
All three scripts download in parallel while the HTML parses. They execute in order after parsing completes.
Anchor to Use ,[object Object], for independent third-party scriptsUse async for independent third-party scripts
async for independent third-party scriptsScripts with async download in the background and execute as soon as they finish downloading. There's no guaranteed execution order, and execution can still briefly block the main thread.
Use async for scripts that are fully independent, don't rely on the DOM, and don't depend on other scripts. Analytics and tracking pixels are the most common examples.
If a third-party script does depend on the DOM or on other scripts, then use defer instead.
Anchor to Know what not to deferKnow what not to defer
Some scripts must run synchronously by design:
- A/B testing anti-flicker snippets. These hide page content until a variant is selected. Deferring them causes a flash of the original content before the variant applies. If you aren't actively running a test, remove the snippet entirely.
- Scripts that modify content in the initial viewport before render. If a script rewrites DOM that's visible without scrolling, then deferring it causes a visible flash or layout shift.
Anchor to Remove scripts that aren't neededRemove scripts that aren't needed
Before deciding whether to defer a script, ask whether it's needed at all. Every script, even deferred, consumes bandwidth and main-thread time during execution. Removing unnecessary scripts is always better than deferring them.
See Audit and remove costly third-party scripts for guidance on evaluating third-party script cost.
For scripts tied to specific user interactions, such as chat widgets, video players, and dialogs, consider loading them only when the user interacts with the feature. See Load JavaScript on user interaction for that pattern.
Anchor to ExamplesExamples
Anchor to Example 1: Theme script loading in ,[object Object]Example 1: Theme script loading in theme.liquid
theme.liquidBefore: Synchronous scripts in <head> block HTML parsing.
After: Deferred scripts download in parallel and execute in order after parsing.
Anchor to Example 2: Third-party analyticsExample 2: Third-party analytics
Before: A synchronous analytics script in <head> blocks page rendering.
After: async lets the browser continue parsing while the script downloads. Because the analytics script is independent and doesn't rely on execution order, async is appropriate here.
Anchor to Example 3: Conditional script loadingExample 3: Conditional script loading
Not every page needs every script. Use the Liquid template object to load scripts only on pages that require them. This reduces both download and execution cost on pages where the script does nothing.
Anchor to TestingTesting
- Network panel waterfall: Open Chrome DevTools, go to the Network panel, and reload the page. Synchronous scripts show a solid bar that blocks subsequent resource loading. Deferred scripts download in parallel with other resources.
- Performance panel, Insights tab: Record a page load and look for Render blocking requests in the summary. Any script without
deferorasyncappears here. - Before and after comparison: Record a Performance panel trace with synchronous scripts, add
defer, then record again. Compare FCP and LCP between the two recordings.
Anchor to ReferencesReferences
asset_urlfilterscript_tagfiltertemplateobjectsettingsobject- Debugging common causes for slow loading in Shopify Liquid storefronts
- Load JavaScript on user interaction
- Manage render-blocking resources strategically
- Manage A/B testing performance impact
- Audit and remove costly third-party scripts