Comparsion between SVG Icon loading strategies. The results may differ when using more, less, smaller or bigger icons. Even combinations of different strategies e.g. using single embeds for big SVGs and sprites for small icons have different results. There also are some benefits in lazy loading single files only when needed (in viewport) that are not covered by the results (only described in pros/cons). The test assumes efficient compression strategies for the HTML and Asset Files e.g. gzip or Brotli. It also assumes the page is served via HTTP/2 so multiplexing is enabled. Preloading content also could have a positive effect. Sometimes even using PNG files could make more sense.
https://svg-loading-strategies.vercel.app
Each icon is loaded separately via JavaScript and embedded into to the DOM.
Before initialization
<svg data-url="asset-url.svg"></svg>After initialization
<svg><path d="M0 0h24v24H0z"></path></svg>| Pros | Cons |
|---|---|
| Efficient Caching since each Icon will get resolved by a separate GET request. | Browser has to work a lot since after download it has to manipulate the DOM. Also depending on the implementation it maybe has to parse the SVG response first. |
| It is possible to just embed the SVG icons needed on the current page | Only works when JavaScript is available. |
| Works asyncronously | Lots of network requests. |
| More granular progressive loading because of the size of the assets compared to a sprite. |
Simple image tag with lazy loading attribute.
<img src="asset-url.svg" loading="lazy" alt="..." />| Pros | Cons |
|---|---|
| Efficient Caching since each Icon will get resolved by a separate GET request. | Styling of the icons is not possible. |
| It is possible to just embed the SVG icons needed on the current page | Lots of network requests. |
| Works asyncronously. | |
| More granular progressive loading because of the size of the assets compared to a sprite. | |
| No JavaScript needed for modern browsers with natively supported lazy loading |
Sprite embedded in body
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
style="position: absolute; width: 0; height: 0"
aria-hidden="true"
>
<defs>
<symbol
id="asset-id"
xmlns="http://www.w3.org/2000/svg"
height="24"
viewBox="0 0 24 24"
width="24"
>
<path d="M0 0h24v24H0z" fill="none"></path>
</symbol>
...
</defs>
</svg>SVG icons using the reference
<svg viewBox="0 0 24 24" width="24" height="24">
<use xlink:href="#asset-id" />
</svg>Sprite is embedded server side and is delivered with each HTML file. The HTML should look the same as in strategy 4 when loading with JavaScript.
| Pros | Cons |
|---|---|
| Bigger DOM is blocking the FCP. | Each icon is loaded even when not needed on the current page. |
| No JavaScript needed. | Inefficient caching (client side) since the same icon is not cacheable by the client when visiting different sites. |
| When using the same icon on one page twice it is a better choice compared to single embeds. | Server has more work to do before cache when compressing the same iconset for lots of pages again and again. |
Sprite is loaded asynchronously and embedded to the DOM by JavaScript
Sprite after embedded by JS in body
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
style="position: absolute; width: 0; height: 0"
aria-hidden="true"
>
<defs>
<symbol
id="asset-id"
xmlns="http://www.w3.org/2000/svg"
height="24"
viewBox="0 0 24 24"
width="24"
>
<path d="M0 0h24v24H0z" fill="none"></path>
</symbol>
...
</defs>
</svg>SVG icons using the reference
<svg viewBox="0 0 24 24" width="24" height="24">
<use xlink:href="#asset-id" />
</svg>| Pros | Cons |
|---|---|
| Efficient Caching (server side) since the sprite is build and compressed once. Clients will request the same sprite for each page. | Each icon is loaded even when not needed on the current page. |
| Works asynchronously. | Browser has to work a lot since after download it has to parse the JS containing the SVG-Sprite and manipulate the DOM. (maybe loading the SVG from a separate SVG file and not from a JavaScript file is a better solution) |
Embed each icon when needed.
<svg><path d="M0 0h24v24H0z"></path></svg>* When embedding the 137 icons 3 times the transferred data increased from 22 to 56kB and when using 6 timess the transferred data increased from 22 to 111kB.
| Pros | Cons |
|---|---|
| Efficient Caching (server side) since the SVG is embedded. | Larger HTML files compared to the strategies with placeholder functionality. |
| No extra network requests. | Inefficient caching (client side) since the same icon is not cacheable by the client when visiting different sites. (maybe gzip/brotli will help for the same Icon on the same page) |
| Easy to implement. | Server has more work to do before cache when compressing the same icon for lots of pages again and again. |
| No JavaScript needed. |
Loading 137 SVG Icons from the google Material design library with different strategies. There is an index.html with each strategy shown in a separate iframe. Because of network concurrency i would not recommend to use this for comparison reasons. If you want to get an impression on how the loading strategy will affect the loading behaviour in your browser use the separate scenarios.
| Lazy Embeds | Images | Sprite Server Side | Sprite JS | Embeds | |
|---|---|---|---|---|---|
| Requests | 140 | 139 | 2 | 3 | 2 |
| Transferred Data | 88.1kB | 105kB | 22.6kB | 27.5kB | 20.7kB |
The following table contains Lighthouse results generated by Lighthouse CLI. The results are representing the median of 50 tests per Site. If you get better results when loading in the browser, Lighthouse CLI emulates a mid-tier mobile device with CPU and Network throttling.
| Lazy Embeds | Images | Sprite Server Side | Sprite JS | Embeds | |
|---|---|---|---|---|---|
| Performance score | 100 | 100 | 100 | 100 | 100 |
| First Contentful Paint | 1.012s | 1.246s | 1.040s | 857ms 🏃 | 1.030s |
| Speed Index | 1.026s | 1.596s | 1.040s | 883ms 🏃 | 1.030s |
| Largest Contentful Paint | 1.162s | 1.246s | 1.040s | 1.247s 🐢 | 1.030s 🏃 |
| Time to Interactive | 1.012s 🏃 | 1.246s | 1.055s | 1.247s 🐢 | 1.030s |
| Total Blocking Time | 0 | 0 | 0 | 100ms 🐢 | 0 |
| DOMContentLoaded Event | 248ms | 256ms | 209ms 🏃 | 339ms 🐢 | 213ms |
| Load Event | 310ms | 535ms 🐢 | 225ms 🏃 | 347ms | 251ms |
There is no recommendation i wanted to give in the first place. I just wanted to know if there are major differences since we evaluated different solutions to load SVG files for an upcoming project. There are way to many parameters to consider before deciding for one solution as i mentioned in the intro. Even compression time on the server could be a valid parameter.
Using the JavaScript Sprite strategy seemed to be the go-to solution for a lot of our projects since there are handy loaders for Webpack doing the work for you. But to avoid using JavaScript if there are other possibilities is often something to consider. Especially when thinking about performance since the Browser always has to work a little more. That also should be the reason for the worst "Totel Blocking Time" and "Time to Interactive" for the SVG Sprite via JavaScript strategy.
