prudkohliad

How I optimized SVG icons on my website

| 183 views

So I was looking at my website the other day, at the bookmarks page in particular. What I realized was that every bookmark had an embedded SVG icon next to it:

Places where embedded SVG elements are located
Places where embedded SVG elements are located

That little arrow telling you that the link will open in a new tab actually looks like this:

<svg
  id="svg-icon-external-link"
  viewBox="0 0 24 24"
  fill="currentColor"
>
  <path d="M8 7C8 6.44772 8.44772 6 9 6L17 6C17.5523 6 18 6.44772 18 7V15C18 15.5523 17.5523 16 17 16C16.4477 16 16 15.5523 16 15V9.41421L7.70711 17.7071C7.31658 18.0976 6.68342 18.0976 6.29289 17.7071C5.90237 17.3166 5.90237 16.6834 6.29289 16.2929L14.5858 8L9 8C8.44772 8 8 7.55228 8 7Z"></path>
</svg>

And since there are 20 bookmarks per page, this thing is duplicated all over the page. So inefficient! This made me wonder if there was a way to have only one definition of the icon on the page and just reference it from all those 20 places. And as it turned out, there indeed was a way!

Enter <use> and <symbol> elements

As MDN says, the <use> element takes nodes from within the SVG document and duplicates them somewhere else. The effect is the same as if the nodes were deeply cloned into a non-exposed DOM and then pasted where the <use> element is.

It can be used in combination with symbol and defs elements that define the “template” to reuse an SVG. In other words - the <symbol> is an “object” and the <use> works as a “pointer” to that object.

SVG <symbol> referenced by <use> pointers
SVG <symbol> referenced by <use> pointers

The implementation

So, I created an <svg> at the beginning of the <body> with all the icons that I have inside as <symbol> elements within a <defs>:

<!-- At the beginning of the <body> element -->
<svg width="0" height="0">
  <defs>
		<!-- The icon template definition -->
    <symbol
      id="svg-icon-external-link"
      viewBox="0 0 24 24"
      fill="currentColor"
    >
      <path d="M8 7C8 6.44772 8.44772 6 9 6L17 6C17.5523 6 18 6.44772 18 7V15C18 15.5523 17.5523 16 17 16C16.4477 16 16 15.5523 16 15V9.41421L7.70711 17.7071C7.31658 18.0976 6.68342 18.0976 6.29289 17.7071C5.90237 17.3166 5.90237 16.6834 6.29289 16.2929L14.5858 8L9 8C8.44772 8 8 7.55228 8 7Z"></path>
    </symbol>

		<!-- ... Other icons ... -->
  </defs>
</svg>

Every icon must have a unique id because we are going to pass it to our <use> elements as a href attribute to point to the definitions. Also notice the width="0" height="0" attributes - if they are not specified, the icon definitions will take actual space in the document, which might affect the layout of your page.

I also replaced every embedded <path> with a <use> pointing to the id of the according <symbol> (with a # at the beginning of the id):

<svg height="18" width="18">
  <!-- Instead of embedding a <path> element -->
  <use href="#svg-icon-external-link" />
</svg>

The result

The page still looks the same:

Icons after the optimization
Icons after the optimization

But, we managed to shave a couple of kilobytes off the page though (13.5 kB before vs 11.4kB after):

Chrome web inspector showing the difference between page sizes
Chrome web inspector showing the difference between page sizes

Considerations

Of course, such optimization will most likely be negligible in this case, but it is a great technique to have in your arsenal for situations when you have an SVG-heavy page (like Twitter, for example, with all their “like” and “repost” buttons).

Another great thing about this technique is that it promotes the DRY principle - you have only one thing and reuse it if necessary. If the icon needs to be changed, you only have to edit a single place in the codebase.

Share this post