Defer render-blocking CSS and Java Script
Be strategic about render-blocking resources. Defer non-critical JavaScript using async or defer, and defer non-critical CSS using the async pattern. Keep CSS for content visible in the initial viewport render-blocking to prevent flash of unstyled content.
When the browser parses HTML and encounters a standard <link rel="stylesheet"> or a synchronous <script src="">, it must pause parsing the HTML, download and process the resource, then resume parsing. This delay directly affects how quickly First Contentful Paint (FCP) can occur.
CSS blocks rendering to prevent Flash of Unstyled Content (FOUC), where HTML renders with default browser styles instead of custom styles, and to prevent layout shifts from late-arriving styles. Critical CSS for content visible in the initial viewport should remain render-blocking. Chrome DevTools might flag render-blocking resources, but that doesn't mean you should eliminate all of them. Critical CSS should block rendering. Evaluate each resource individually.
Keep critical CSS for content visible in the initial viewport render-blocking, and make non-critical styles non-blocking. Use section.index to conditionally apply the async CSS pattern to section styles outside the initial viewport, because the first sections render above the fold and their styles are critical.
Test before and after to confirm that you haven't introduced FOUC or CLS.
Anchor to JavaScriptJava Script
Defer scripts that aren't essential for the initial render, such as analytics or chat widgets. The script_tag filter doesn't support defer or async attributes, so write the <script> tag manually with defer:
The defer attribute tells the browser to download the script in parallel with HTML parsing and execute it after parsing finishes, so it doesn't block rendering.
For more guidance, see Defer non-critical scripts.
Anchor to What you can't deferWhat you can't defer
Some resources aren't under your control as a theme developer, so you can't defer them:
- Shopify injects
content_for_header, and you can't defer it. - App scripts injected through the ScriptTag API load independently of theme control. Ask the app developer to use
deferif a script blocks rendering. - A/B testing anti-flicker snippets must load synchronously by design, because they prevent a flash of original content before the variant applies. See Manage A/B testing performance impact.
Anchor to Decide what to deferDecide what to defer
Use this table to decide whether a resource is a good candidate for deferral:
| Resource type | Typically deferrable? | How to defer in Shopify |
|---|---|---|
| Section CSS (below fold) | Yes | Async CSS pattern with section.index |
| Section CSS (above fold) | No | Keep synchronous |
| Theme interaction JS | Yes | Manual <script defer> tag (the script_tag filter doesn't support defer) |
| Analytics and tracking | Yes | Load after DOMContentLoaded or on interaction |
| App scripts (ScriptTag API) | No (not theme-controlled) | Ask app developer to use defer |
content_for_header | No | Platform-managed, not deferrable |
Anchor to ExamplesExamples
Async CSS pattern for content outside the initial viewport (using the media print trick):
Use section.index for position-aware optimization. See Use section.index for position-aware loading for conditional rendering.
Anchor to TestingTesting
- Use the Chrome DevTools Performance panel to identify render-blocking resources. After recording a load, check the Render blocking requests section in the Summary tab.
- Use the Chrome DevTools Network panel to visualize the waterfall and identify which resources are delaying the critical path (FCP or LCP).
- Compare FCP before and after your changes.
- Visually inspect the page to confirm that your changes haven't introduced FOUC or new layout shifts.
Anchor to ReferencesReferences
- Debugging common causes for slow loading in Shopify Liquid storefronts
- Defer non-critical scripts
- Load critical CSS synchronously
- Use
section.indexfor position-aware loading