How I optimized SVG icons on my website
Tags: javascript, nextjs, performance
Published: 2024-05-08 16:01:30

So I was looking at my website the other day, at the [bookmarks](https://www.prudkohliad.com/bookmarks) page in particular. What I realized was that every bookmark had an embedded SVG icon next to it:

<figure>
  <img src="https://www.prudkohliad.com/images/articles/reuse-svg-icons-2023-08-14/1.webp" alt="Places where embedded SVG elements are located" />
  <figcaption>Places where embedded SVG elements are located</figcaption>
</figure>

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

```html
<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 &lt;use&gt; and &lt;symbol&gt; elements

As [MDN says](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/use), 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](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/symbol) and [defs](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/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.

<figure>
  <img src="https://www.prudkohliad.com/images/articles/reuse-svg-icons-2023-08-14/2.webp" alt="SVG &lt;symbol&gt; referenced by &lt;use&gt; pointers" />
  <figcaption>SVG &lt;symbol&gt; referenced by &lt;use&gt; pointers</figcaption>
</figure>

## 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>`:

```html
<!-- 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):

```html
<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:

<figure>
  <img src="https://www.prudkohliad.com/images/articles/reuse-svg-icons-2023-08-14/3.webp" alt="Icons after the optimization" />
  <figcaption>Icons after the optimization</figcaption>
</figure>

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

<figure>
  <img src="https://www.prudkohliad.com/images/articles/reuse-svg-icons-2023-08-14/4.webp" alt="Chrome web inspector showing the difference between page sizes" />
  <figcaption>Chrome web inspector showing the difference between page sizes</figcaption>
</figure>

## 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](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) 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.

[My original blog post](https://www.prudkohliad.com/articles/reuse-svg-icons-2023-08-14#considerations)