Inline Critical CSS in Liquid

A new feature has arrived in Liquid, and with it some new possibilities.

On the off chance your town crier hasn't been screaming "New filter in Liquid!" this week, here's the news: there's a new filter available in Liquid.

Thomas Kelly requested a filter to inline SVG assets in Shopify themes recently, and in the blink of an eye a gift was delivered in the form of inline_asset_content.

Much of the discussion around inline_asset_content is for applying this to SVG rendering in your Shopify storefront, but you might notice that inline_asset_content isn't SVG specific. The filter actually works with JavaScript and CSS files too. Meaning you can render your CSS inside a <style> tag.

<style>
  {{ 'section-hero-banner.css' | inline_asset_content }}
</style>

Why bother inlining CSS?

I certainly wouldn't recommend inlining all of your CSS, but with this new feature you might want to explore inlining some styles for elements above the fold - commonly referred to as critical CSS. This way your browser isn't blocked from rendering your page while it fetches those CSS files.

The concept of inlining critical CSS has been around for a number of years and I'd enourage you to read more about it first. "Understanding Critical CSS" by Dean Hume is a good article to get you started.

How would I implement this in Liquid?

With the relatively new section.index0 - or section.index if you prefer - you can conditionally inline your critical CSS. This way if a merchant wants section A, B and C at the top of one landing page, but sections X, Y and Z on another, those styles can both be inlined appropriately on those pages, without manual control from you, the developer. Here's an example of how this might look:

{%- # sections/hero-banner.liquid -%}

{%- if section.index0 < 2 -%}
  <style>
    {{ 'section-hero-banner.css' | inline_asset_content }}
  </style>
{%- else -%}
  {{ 'section-hero-banner.css' | asset_url | stylesheet_tag }}
{%- endif -%}

Why not write my CSS directly into a Liquid snippet?

You've already been able to achieve CSS inlining for a long time - simply by writing the styles directly into a Liquid snippet. So why bother with this new technique?

There are some benefits to using the new inline_asset_content filter:

  1. The CSS is minified for you automatically. This was a pleasant surprise to me. There is at least one existing Liquid filter technique for minifying CSS, but personally I've always worried it's missing edge cases that would trip me up a year or so down the line.
  2. Your styles all remain in a predictable place, rather than dotted around both Liquid snippets and CSS files.
  3. You can take advantage of tools like Sass, PostCSS and Stylelint - whichever tools you personally enjoy using.
  4. You can conditionally inline the styles. This provides more flexibility to solve performance problems you might encounter.

A word of warning

People are often looking for silver bullets with performance on the web, and this technique is no silver bullet. As with a lot of performance techniques, this comes with its own set of drawbacks.

In this case the most obvious drawback is there are no stylesheets to cache. If a section is used at the top of a lot of pages, and requires a hefty chunk of CSS, those styles need to be rendered in your HTML every single time.

I would strongly recommend reading "Critical CSS? Not So Fast!" by Harry Roberts to learn more from someone with much more expertise in this area.

When it comes to performance on Shopify stores - before implementing any technique like this - you should first understand what the key performance issues are. This technique with inline_asset_content is just another trick in your toolbox.