Shopware 6 Hidden Gems #11: sw_icon_cache — why your SVG icons sometimes break (and how Shopware shrinks your HTML)
Fabian Blechschmidt
This gem is a two-for-one: a neat optimization you didn’t know you were using, and the explanation for a bug you have probably chased — icons that render fine on the page but show up empty when the same template fragment is loaded via AJAX.
The optimization you’re already running
A product listing renders the wishlist heart, the cart icon, arrows, stars — dozens of inline SVGs, many of them the same icon over and over. Every one carries its full markup, including the <defs> block with the actual path definitions. That’s a lot of repeated bytes.
Enter IconCacheTwigFilter (storefront/Framework/Twig/Extension/IconCacheTwigFilter.php). The core’s central icon template pipes every icon through it (utilities/icon.html.twig:22-33):
{{ icon|sw_icon_cache|raw }}
The mechanics (lines 35-50): the filter extracts the icon’s id attribute. The first time an icon id appears in the render, it passes through untouched — full SVG, including <defs>. Every subsequent occurrence of the same id gets its <defs>...</defs> stripped. That works because SVG ids are document-global: the later icons‘ <use> references happily resolve against the defs of the first instance.
Fifty cart icons on a listing page → one set of path definitions, forty-nine lightweight references. Smaller HTML, same pixels, zero configuration.
And it is zero configuration, because StorefrontController::renderStorefront() switches the cache on for every page render (Controller/StorefrontController.php:78-88):
Note the ?? true: this is on by default in every Shopware 6.7 storefront. There is a system-config kill switch (core.storefrontSettings.iconCache) and you can turn it off in Settings > System > Icon Cache.
The bug it explains
Now the flip side. The deduplication assumes all icons end up in the same final document, with the first (complete) instance present. Two situations break that assumption:
Fragments rendered separately — ESI blocks, AJAX endpoints returning HTML (off-canvas cart, wishlist widgets), CMS elements fetched later. If the fragment’s icon was „deduplicated“ during its render because the page render had already seen that icon — but the fragment is inserted into a DOM that doesn’t contain the full instance — the <use> points at nothing. Empty icon.
JavaScript that moves or clones markup into contexts where the defs-carrying first instance isn’t around.
The core knows this, and this is the part that turns the filter from trivia into a tool: templates can switch the cache off locally. Two functions, both undocumented:
{{ sw_icon_cache_disable() }}
{# every icon here renders complete, defs included #}
{% sw_include '@Storefront/storefront/utilities/icon.html.twig' with { name: 'heart' } %}
{{ sw_icon_cache_enable() }}
That’s verbatim what the core does in exactly the fragile spots: the wishlist product box (component/product/card/box-wishlist.html.twig:4,67) and alerts (utilities/alert.html.twig:97,111 — alerts get rendered into <noscript> and AJAX responses). The icon include also accepts an iconCache: false parameter for single includes — see the noscript block in base.html.twig:33.
So if you’ve ever had „the icon shows on the page but not in the off-canvas/AJAX version“ and fixed it by pasting the raw SVG: this was the actual cause, and sw_icon_cache_disable() around your fragment is the actual fix.
When to reach for which switch
Your AJAX-loaded template fragment has broken icons → wrap the fragment’s icons in sw_icon_cache_disable() / sw_icon_cache_enable(), like the core’s wishlist box.
A single fragile include → pass iconCache: false to the icon template.
Debugging icon weirdness in general → set the system config core.storefrontSettings.iconCache to false and see if the problem disappears; if yes, you know which post to re-read.
A custom theme with heavy icon use → do nothing. You’re already benefiting.
TL;DR
Shopware deduplicates inline SVG icons per page render: first occurrence keeps its <defs>, repeats get them stripped and resolve against the first one. It’s on by default (core.storefrontSettings.iconCache ?? true in StorefrontController), saves real bytes on icon-heavy pages — and it’s the reason icons break in AJAX/ESI fragments that don’t contain the first instance. Local fix: {{ sw_icon_cache_disable() }} / {{ sw_icon_cache_enable() }} or iconCache: false on the include, exactly like the core’s own wishlist box does. None of it documented.
Next up: StorefrontRenderEvent — one subscriber to inject variables into every storefront template, no controller inheritance required.
Chased this icon bug before? Drop me an email, misery loves company 😀
Found in Shopware 6.7.0.0: filter logic in storefront/Framework/Twig/Extension/IconCacheTwigFilter.php:35-50, default-on toggle in storefront/Controller/StorefrontController.php:78-88, core usage in Resources/views/storefront/utilities/icon.html.twig:22-33, local disables in component/product/card/box-wishlist.html.twig:4,67 and utilities/alert.html.twig:97,111.