SVG Sprites Uncovered: A Deep Dive into Existing Solutions and Innovations

mohamed sayedmohamed sayed
6 min read

Intro 🎬

In this article, we’ll uncover a powerful technique for image optimization—SVG sprites. Not only will we explore the leading solutions available today, but we’ll also dive into how you can craft your own custom solution to tackle specific challenges.

Problem 🚩

Imagine you are asked to design a dashboard like instabug

Instabug dashboard

As demonstrated in the previous image, the home page contains over 40 icons. If each icon were treated as a separate image, this would result in over 40 individual network requests just for the icons, in addition to requests for bundles and backend resources.

This can easily exceed network limitations, leading to congestion and significantly degrading overall performance by increasing latency and consuming available bandwidth.

Even after loading these icons, we may encounter a scenario where additional icons are needed for another page. This would further impact performance during route changes, resulting in a poor user experience.


Solution 💡

By combining all SVGs into a single file and referencing individual icons as needed, we can limit the network requests to just one, but how 🤔?

SVGs from individual to sprite

by understanding the nature of SVG we will know how to do that.
SVG (Scalable Vector Graphics) are vector-based and use mathematical equations to define shapes and colors.

SVG's mathematical nature enables the sprite feature, which wraps each graphic in a <symbol> element with a unique ID and combines them into a single SVG file.

We have two technique for creating SVG spritemap:
internal reference and external reference

Internal reference:

in internal reference , we create spritemap and inject it to the DOM

Example:

In this example, we place the content of the attachment and bell icons between <symbol> tags, assigning each symbol a unique ID that can be referenced later using xlink:href.

External reference

In external referencing , the spritemap is stored in an external file, and its name is referenced in the DOM.

Example:

  • create spritemap.svg file
<svg  xmlns="http://www.w3.org/2000/svg"  width="0" height="0">
     <symbol id="attachment"  viewBox="0 0 395.449 395.449">
    <path d="M357.744,60.411c-0.01-0.01-0.021-0.021-0.033-0.032c-24.096-24.096-59.338-39.665-89.789-39.666
    c-32.213,0-62.475,12.52-85.213,35.255L28.16,210.517C10.004,228.673,0.004,252.832,0,278.551
    c0.002,25.71,9.996,49.863,28.146,68.015c0.004,0.005,0.01,0.01,0.014,0.015c18.154,18.149,42.313,28.149,68.029,28.156
    c0.018,0,0.037-0.002,0.055,0c25.701-0.012,49.846-10.012,67.988-28.158l137.25-137.243c4.803-4.756,20.479-22.251,20.545-47.657
    c0.03-11.833-3.388-29.394-19.849-45.855c-18.477-18.477-37.973-21.26-51.075-20.341c-15.914,1.116-31.23,8.282-43.128,20.18
    L96.251,227.384c-7.811,7.812-7.811,20.474,0,28.284c7.811,7.81,20.473,7.811,28.285,0l111.726-111.727
    c5.006-5.005,11.437-8.125,17.642-8.562c6.987-0.49,13.715,2.445,19.991,8.724c5.412,5.412,8.15,11.288,8.134,17.467
    c-0.025,10.242-7.244,17.907-8.759,19.408L135.945,318.295c-10.588,10.591-24.682,16.429-39.695,16.439
    c-0.035-0.002-0.07,0-0.105-0.002c-15.014-0.016-29.111-5.854-39.705-16.443c-0.004-0.004-0.004-0.004-0.008-0.008
    C45.836,307.686,40,293.574,40,278.553c0.002-15.031,5.842-29.147,16.445-39.752l154.549-154.55
    c15.18-15.179,35.397-23.539,56.928-23.539c19.76,0,45.047,11.493,61.512,27.956c0.007,0.007,0.015,0.015,0.021,0.021
    c16.033,16.035,25.994,38.852,25.994,59.549c0,21.53-8.357,41.747-23.539,56.926l-53.988,53.979
    c-7.812,7.812-7.813,20.474-0.002,28.284c0,0.001,0,0.001,0.002,0.002c7.81,7.811,20.471,7.812,28.279,0.001l53.992-53.981
    c22.735-22.734,35.256-52.997,35.256-85.211C395.449,116.85,381.354,84.02,357.744,60.411z"></path>
  </symbol>
  <symbol id="bell" fill="#000000" viewBox="0 0 500 500">
    <path d="M 366.25,212.825c-57.00-122.675-86.625-169.275-179.775-167.325c-33.175,0.675-25.20-24.025-50.50-14.65 C 110.70,40.20, 132.40,53.925, 106.525,75.15c-72.55,59.55-65.875,114.65-32.225,246.00c 14.20,55.30-34.175,58.025-15.05,111.65 c 13.975,39.075, 116.975,55.475, 225.65,15.125c 108.675-40.30, 177.25-120.325, 163.275-159.45C 429.05,234.875, 390.275,264.475, 366.25,212.825z M 273.10,414.90 c-97.025,36.00-176.80,14.825-180.175,5.425c-5.80-16.225, 31.325-70.40, 142.275-111.575c 110.95-41.175, 172.875-25.90, 179.35-7.775 C 418.375,311.675, 370.175,378.875, 273.10,414.90z M 241.925,327.55c-50.75,18.825-86.00,40.35-108.825,59.75 c 16.075,14.60, 46.15,18.15, 76.15,7.025c 38.175-14.175, 61.625-46.70, 52.35-72.625c-0.10-0.325-0.275-0.60-0.40-0.90 C 254.95,322.825, 248.525,325.075, 241.925,327.55z"></path>
  </symbol>
</svg>
  • utilize external spritemap in your document
<!DOCTYPE html>
<html lang="en">
<head>
  <title>spritemap external</title>
</head>
<body>
  <svg>
    <use xlink:href="spritemap.svg#attachment"></use>
  </svg>
  <svg>
    <use xlink:href="spritemap.svg#bell"></use>
  </svg>
</body>
</html>

There are many online tools available that can automatically generate SVG sprite maps. One such tool is SVGSprite

Limitation:
If your SVG includes a <LinearGradient> element, the linear gradient color may not be rendered correctly. (browser support issue)


Thought 💭

I can read questions running on your mind now
How can I use this approach in production if I have a thousand icons?”. “Should I upload them to one of these tools and then append the output to my root file?“ ☹️

Definitely short answer is NO 🙅

Uploading images is just one part of the challenge. Another issue is managing updates: if you add a new icon to your folder, you need to regenerate the spritemap or manually create a new <symbol> and include the icon's content within it.

We need to set up a task runner that executes each time we build our project to handle generating spritemap image automatically. Here, Webpack's role truly shines.

There are several plugins available that serve this purpose. Below, we will explore how each plugin functions, their respective drawbacks, and the reasons behind our decision to develop a new solution


webpack-svgstore-plugin 🔗

Initially, we used a plugin based on the internal referencing technique. You can refer to their doc for setup and usage instructions.

BUT

You need to take the following factors into consideration:

  1. Project scale: if your SVGs folder contains a large number of icons, all of them will be injected into the DOM, leading to DOM bloating and potential performance issues.

  2. Webpack version: if you're using Webpack5 or planning to migrate to it, you'll need to find an alternative solution. This plugin doesn't support Webpack5 and the maintainer has no plans to support it, as the plugin's repository was archived on March 18, 2023.


svg-spritemap-webpack-plugin 🔗

Both previous issues are unacceptable, especially for large web applications where performance is a necessity, not a luxury.

Therefore, we searched for a plugin that utilizes an external reference technique.

@ Instabug, we explored adopting svg-spritemap-webpack-plugin in our codebase, but we encountered an issue with ID collisions.

Let’s walk through a simple example to illustrate the issue, Consider the following two SVG icons:

“What happened ?!” , “why info icon disappeared ?!!” 😲

The issue arises because both <path> tags inside the <symbol> elements have the same id ("path-1"), causing conflicts when referenced. The browser displays the same icon twice since the <use> element refers to the first matching id.

Solution:
We need to append the SVG file name to the ID, as the file name is unique for each SVG

SO , what are the problems of svg-spritemap-webpack-plugin ?
let’s examine this code

// code snippet from svg-spritemap-webpack-plugin
const config = generateSVGOConfig(
  outputOptions.svgo,
  [
    {
      name: 'removeTitle',
      active: !spriteOptions.generate.title, 
    },
  ],
  [
    {
      name: 'cleanupIDs',
      active: false,
    },
  ],
)

In this snippet, svg-spritemap-webpack-plugin utilizes SVGO for optimizing SVG files, but it disables the cleanupIDs plugin, which leads to the ID collision issue we previously discussed.

The previously mentioned issue is not the only limitation of the
svg-spritemap-webpack-plugin. It overlooks the linearGradient problem we discussed earlier, and continues to include SVGs containing linearGradient elements in the spritemap output without addressing the issue.


simple-svg-sprite 🔗

For more flexibility, we have decided to develop our own simple custom solution. Building a spritemap algorithm is relatively straightforward and will allow us to make adjustments easily, without relying on maintainer support.

Generate spritemap algorithm

spritemap algorithm

It can be employed in any project that uses Webpack as a bundler, providing a general solution for generating spritemap .This is not specifically customized for the instabug codebase.

Check this demo to discover more

Below is a summary comparing the three plugins:

webpack-svgstore-pluginsvg-spritemap-webpack-pluginsimple-svg-sprite
reference methodInternalExternalExternal
Webpack support4 only4 and 54 and 5
ID collisionsNOT ExistedExistedNOT Existed
Unpacked Size75 kB79.6 kB7.23 kB
0
Subscribe to my newsletter

Read articles from mohamed sayed directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

mohamed sayed
mohamed sayed

Software engineer at Instabug | Frontend team. Passionate about web development.