Critical CSS
Goal: render the styles needed for first paint inline, so they do not block rendering and cost no request, while the rest of the CSS loads normally.
Shopify's inline_asset_content filter inlines an asset only when it is SVG, JS, or CSS and smaller than 15 KB (reference). That fits a small critical stylesheet, not a full bundle.
Steps
Put only above-the-fold rules (layout shell, header, typography, tokens) in a small stylesheet.
Declare it as an inline entry next to the main stylesheet:
ts// vite.config.ts shopify({ entries: { 'critical.css': { source: 'frontend/entrypoints/critical.css', inline: true }, 'theme.css': 'frontend/entrypoints/theme.css', 'theme.ts': 'frontend/entrypoints/theme.ts', }, });Render both in
<head>, critical first:liquid{% render 'vite-tag', entry: 'critical.css' %} {% render 'vite-tag', entry: 'theme.css' %}Run
npm run buildand opensnippets/vite-tag.liquid: thecritical.cssblock contains a<style>withinline_asset_content, andtheme.cssa normal<link>.
Behavior to know
- The built file stays in
assetsand is recorded as plugin-owned; the filter reads it from the theme at render time. - In development the entry is a normal Vite
<link>with HMR, so you see the same styles without inlining. - At 15,000 bytes or more the build warns with
INLINE_CSS_TOO_LARGEand renders a<link>instead. Shrink the stylesheet or removeinline. - With
build.cssCodeSplit: false, all stylesheets merge into one aggregated file andinlineis ignored. - The rendered page grows by the size of the inlined CSS; the snippet itself stays tiny, so the 256 kB snippet limit is unaffected.