Load critical CSS synchronously
Load critical CSS synchronously. Apply the async CSS pattern, where media="print" with onload switches the media to all, only to sections outside the initial viewport after testing proves it's beneficial.
CSS is render-blocking by design, to prevent a jarring Flash of Unstyled Content (FOUC). When async CSS loads for content visible in the initial viewport:
- Content renders with missing styles.
- Styles arrive late and cause re-renders.
- Content shifts to accommodate new styles, which increases CLS.
- FOUC confuses users.
- It can trigger unnecessary image loads, because of
IntersectionObserverconfusion.
The goal isn't to eliminate all render-blocking CSS, but to be strategic about what blocks rendering. Critical CSS is the minimum set of styles required to render the content visible in the initial viewport correctly, and it should always be render-blocking.
Rule: if the stylesheet styles anything users see before scrolling, then load it synchronously (render-blocking). Defer only non-critical styles for content outside the initial viewport.
The async CSS pattern works like this:
- The browser sees
media="print". - It sets download priority to Lowest.
- Initial render doesn't use these styles.
- After the load event, JavaScript changes
mediatoall. - Priority changes to Highest.
- The page re-renders with the new styles.
Load critical CSS for content visible in the initial viewport synchronously (render-blocking). Defer non-critical CSS for content outside the initial viewport asynchronously only if testing proves it's beneficial.
Use section.index to conditionally apply async CSS:
The condition tests section.index > 3 rather than section.index <= 3. section.index is nil in the online store editor, in static sections, and in the Section Rendering API, and comparisons against nil are falsey, so {% if section.index <= 3 %} would load the stylesheet asynchronously in those contexts. Writing the async case as the positive test keeps nil on the synchronous side. For the full explanation, refer to Load above-the-fold and below-the-fold sections differently with section.index.
For sections where you're unsure whether async loading is safe, a conservative approach is to place the <link rel="stylesheet"> tag immediately before the section's content. The browser can render everything above that point while the preload scanner picks up the stylesheet.
Anchor to ExamplesExamples
Anchor to Real-world: Dawn theme CLS improvementReal-world: Dawn theme CLS improvement
As a real-world example, the Dawn theme had async CSS applied to components visible in the initial viewport. When this was discovered in PR #2270, it was causing visible layout shifts:
"You may notice the whole 'Obsessive Attention. Intelligent Effort.' shifting to the right in the control run because of asynchronous
component-rte.css."
Removing async CSS from content visible in the initial viewport improved CLS by 95 percent, from 0.065 to near-zero. The async pattern had been applied in too many places throughout the theme, causing unstyled content to appear and shift when styles loaded. This is especially problematic on first visits and on slower connections.
Anchor to Async CSS patternAsync CSS pattern
Anchor to When to useWhen to use
- Sections outside the initial viewport, section 4 and later.
- Non-critical styling.
- After testing proves it's beneficial.
Anchor to When not to useWhen not to use
- The first 3 sections.
- Content visible in the initial viewport.
- Critical page styling.
- Without before-and-after testing.
Anchor to TestingTesting
Before implementing the async CSS pattern:
- Open the Insights tab in the Chrome DevTools Performance panel.
- Record a page load, and note CLS and visual rendering. Use the View Trace button for details.
- Implement async CSS.
- Record again and compare FCP, LCP, and CLS.
- Check RUM data after deployment.
Use the Performance panel filmstrip view to see visual shifts. Compare with and without the async pattern. Check CLS in RUM data after deployment. The pattern might hurt more than it helps, so always verify.
Anchor to ReferencesReferences
- Dawn PR #2270: removed async CSS from content visible in the initial viewport (95 percent CLS improvement).
iftagsection.indexasset_urlfilter- How to optimize Cumulative Layout Shift (CLS) on Shopify sites
- How layout position impacts three big web performance levers
- Announcing new Liquid features for better web performance
- Defer non-critical resources
- Use
section.indexfor position-aware loading - Never lazy-load the LCP image
- Reduce stylesheet count