Optimizing SVGs for the best frontend performance

With SVGs a popular choice for rendering visual assets, as opposed to images, you should optimize your SVGs and select the most approriate method to render them in your application.

Why is this important?

Because rendering SVGs incorrectly can have a massive negative performance impact on your site. When implemented incorrectly, they can hurt the rendering performamce, increase memory usage, and increase the overall size of your JS bundle.

Let's examine the 5 key takeaways I discovered while doing some research into this topic.

1. Always optimize your SVGs.

This is the most important takeaway. We want our SVGs to be as small as possible. We can do this by optimizing their structure and removing unecessary data. Many times, SVGs will have unnecessary tags or grouping which contribute to the overall size.

// unoptimized
<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">
  <defs></defs>
  <g>
    <title>Red Circle</title>
    <desc>A simple red circle</desc>
    <rect width="100%" height="100%" fill="white" />
    <circle cx="50" cy="50" r="40" stroke="black" stroke-width="3" fill="red">
      <title>Circle Title</title>
      <desc>This is a red circle</desc>
    </circle>
  </g>
  <defs></defs>
</svg>

// optimized
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
  <circle cx="50" cy="50" r="40" stroke="#000" fill="#f00" />
</svg>

Notice how in the unoptimized example, we have a number of unnecessary tags and poor markup structure. This can be particularly egregious in complex SVGs.

There are great tools which can help optimize SVGs, such as:

  • Scour, which is Python based
  • SVGO, which is similar but based in NodeJS.

If you're working with a designer, you may also want to consult with them to see how they can optimize SVGs they are creating before they are handed off to you.

2. Don't use SVGs as JSX.

It's anti-pattern. SVGs aren't HTML, they are XML that describes an image. It's the most expensive form of sprite sheet: costs a minimum of 3x more than other techniques, and hurts both runtime (rendering) performance and memory usage

export const IconTrash = (props: any): JSX.Element => (
  <svg
    width='24'
    height='24'
    viewBox='0 0 24 24'
    xmlns='http://www.w3.org/2000/svg'
    {...props}>
    <path d='M19 24h-14c-1.104 0-2-.896-2-2v-17h-1v-2h6v-1.5c0-.827.673-1.5 1.5-1.5h5c.825 0 1.5.671 1.5 1.5v1.5h6v2h-1v17c0 1.104-.896 2-2 2zm0-19h-14v16.5c0 .276.224.5.5.5h13c.276 0 .5-.224.5-.5v-16.5zm-9 4c0-.552-.448-1-1-1s-1 .448-1 1v9c0 .552.448 1 1 1s1-.448 1-1v-9zm6 0c0-.552-.448-1-1-1s-1 .448-1 1v9c0 .552.448 1 1 1s1-.448 1-1v-9zm-2-7h-4v1h4v-1z' />
  </svg>
);

export const iconTrash = (
  <svg
    width='24'
    height='24'
    viewBox='0 0 24 24'
    xmlns='http://www.w3.org/2000/svg'>
    <path d='M19 24h-14c-1.104 0-2-.896-2-2v-17h-1v-2h6v-1.5c0-.827.673-1.5 1.5-1.5h5c.825 0 1.5.671 1.5 1.5v1.5h6v2h-1v17c0 1.104-.896 2-2 2zm0-19h-14v16.5c0 .276.224.5.5.5h13c.276 0 .5-.224.5-.5v-16.5zm-9 4c0-.552-.448-1-1-1s-1 .448-1 1v9c0 .552.448 1 1 1s1-.448 1-1v-9zm6 0c0-.552-.448-1-1-1s-1 .448-1 1v9c0 .552.448 1 1 1s1-.448 1-1v-9zm-2-7h-4v1h4v-1z' />
  </svg>
);

This not only includes the SVG in your final bundle, but it also requires Javascript to render.

If you include SVGs in your code, they have to be parsed and compiled alongside the rest of your code. This translates into your users waiting longer for your website or app to become interactive.

3. Inline SVGs are best if you have complex SVGs, or need all the bells and whistles.

Okay, I know we said we should keep SVGs out of our JS bundles, but sometimes it unavoidable, especially if you need to manipulate the contents of the SVG, transform styles, attach event handlers, etc. Make sure they are well optimized though. Additionally, these might be a good choice for a logo or another icon that need to be immediately visible to the user.

However - if you need to inline the SVG, but don't need to transform it, you can inline it using an <img> tag - see #5.

4. SVG sprites are a good alternative if you have a lot icons.

SVG sprites function like image sprites of days long gone, where we group all the SVGs into one file, and then reference them individually by their IDs. SVG sprites also have the advantage of reducing network requests and supporting caching, and also support basic modifications eg fill, background etc.

// ./icons.svg
<svg xmlns="http://www.w3.org/2000/svg" style="display:none">
  <symbol id="circle" viewBox="0 0 24 24">
   <svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
      <circle cx="50" cy="50" r="40" stroke="#000" fill="#f00" />
    </svg>
  </symbol>
</svg>

<svg><use href="./icons.svg#circle"/></svg>

Note that if you're attempting to load the SVGs from another domain with the <use> tag, you'll run into a CORS issue. Jacob 'Kurt' Groß offers a solution for this in his post.

5. Image elements with XML data URIs are a great choice for performance.

According to an analysis by CloudFour, this is by far the fastest technique and most consistent technique of loading SVGs. Conversion to Base64 isn't necessary - we can inline the SVG content directly into the src attribute.

<img src="data:image/svg+xml,..." alt="">

Final Thoughts

Note that the above optimizations are critical for large applications, or applications with many icons (ie hundreds). If you only have a handful of icons or a small application, the performance benefits may be negligible. That being said, we should strive to make our software as performant as possible - so if you're starting a new project it would be best to implement these from the get-go!

References

Below are the articles I read that helped me level up my knowledge of SVGs, and are great reading if you want a more in-depth analysis of everyhing covered here.