Skip to main content

Defer render-blocking CSS and JavaScript

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.

{%- if section.index > 2 -%}
<link rel="stylesheet" href="{{ 'section-styles.css' | asset_url }}" media="print" onload="this.media='all'">
<noscript><link rel="stylesheet" href="{{ 'section-styles.css' | asset_url }}"></noscript>
{%- else -%}
{{ 'section-styles.css' | asset_url | stylesheet_tag }}
{%- endif -%}

Test before and after to confirm that you haven't introduced FOUC or CLS.

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:

<script src="{{ 'theme-interactions.js' | asset_url }}" defer></script>

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 defer if 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 typeTypically deferrable?How to defer in Shopify
Section CSS (below fold)YesAsync CSS pattern with section.index
Section CSS (above fold)NoKeep synchronous
Theme interaction JSYesManual <script defer> tag (the script_tag filter doesn't support defer)
Analytics and trackingYesLoad after DOMContentLoaded or on interaction
App scripts (ScriptTag API)No (not theme-controlled)Ask app developer to use defer
content_for_headerNoPlatform-managed, not deferrable

Async CSS pattern for content outside the initial viewport (using the media print trick):

<link
rel="stylesheet"
href="non-critical.css"
media="print"
onload="this.media='all'"
/>
<noscript><link rel="stylesheet" href="non-critical.css" /></noscript>

Use section.index for position-aware optimization. See Use section.index for position-aware loading for conditional rendering.


  • 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.


Was this page helpful?