Performance Best Practices: Critical CSS, Lazy Loading, Why Hyvä Is Noticeably Faster Than Luma
Performance Best Practices: Critical CSS, Lazy Loading, Why Hyvä Is Noticeably Faster Than Luma
~8 Min. Lesezeit Zuletzt aktualisiert am August 9, 2026
Chapter 1 already explained why Hyvä is fundamentally faster than Luma (less JavaScript, server-side rendering). This chapter goes a step further: concrete techniques to squeeze even more out of that baseline speed.
Critical CSS
Even a built Tailwind styles.css can run to several hundred kilobytes, depending on project size - but only a fraction of it is needed for the very first visible screen area ("above the fold"). Hyvä themes support extracting critical CSS: a small CSS block, inlined into the <head>, containing exactly the rules needed for the visible area on first render. The rest of the CSS file loads asynchronously, without blocking the first render.
Tipp: Critical CSS is worth it mainly for high-traffic entry pages (home page, category pages) - for less-visited pages, the configuration effort often outweighs the actual performance gain.
Lazy loading images
Images outside the visible area (e.g. further down a long category page) should use the native loading="lazy" attribute - the browser then only loads them once they're approaching the visible area, instead of loading every image immediately on page load.
<img src="<?= $escaper->escapeUrl($product->getImageUrl()) ?>"
loading="lazy"
alt="<?= $escaper->escapeHtmlAttr($product->getName()) ?>">Achtung: The first, immediately visible image on a page (e.g. the main product image) should not get loading="lazy" - that would artificially delay the image that's decisive for Largest Contentful Paint. Lazy loading belongs on images that aren't visible on first render anyway.
Appropriate image sizes via view.xml
As mentioned in chapter 4, etc/view.xml defines image sizes per context (product list, product page, gallery thumbnail). A common performance mistake is embedding a large original image everywhere unchanged, instead of using appropriately downsized variants per context - that wastes load time and bandwidth unnecessarily, especially on mobile devices.
Fewer, more targeted Alpine components
Even though Alpine is tiny: a great many, deeply nested x-data regions on a page still add initialization overhead. Rule of thumb: one Alpine component per logically cohesive UI area (like the entire team filtering from chapter 21 living in a single x-data), rather than a separate one for every individual element.
Always test against the production build
The watcher from chapter 5 produces unminified, unoptimized CSS for faster development rebuilds. Performance measurements (Lighthouse, PageSpeed Insights) should therefore always run against the production build (npm run build, followed by the full deploy sequence from chapter 5/27), not against the watcher version - otherwise results come out artificially worse.