SVG in SVG

Published

A nested resource pattern & when to use it

Prelude

We've all been there, you're building a website with your favourite JS framework and want to add an icon button. No worries, just drop an img tag in your button, right? (not the best example, but stay with me)

button.jsx

<button>
  <img src="/icon.svg" />
</button>

Now let's just change the colour on focus... Oh—how the heck do I do that?

What's the problem?

Our icon is an SVG and its colour is set inside the file. To make it match the CSS font colour, we'll need to update the fill and/or stroke to use a special keyword: currentColor.

icon.svg

<svg xmlns="http://www.w3.org/2000/svg"
  width="100"
  height="100"
>
  <circle r="50" cx="50" cy="50" fill="currentColor" />
</svg>

Miles, that didn't work.

Sorry, I forgot to mention that you can't use it in an img tag.

But there's a solution

SVGs are this great and powerful tool: vector graphics as markup. This makes the format familiar and accessible to anyone who knows HTML. Better yet, they are also part of the HTML specification, meaning you can use those tags directly inline.

button.jsx

<button>
  <svg width="100" height="100">
    <circle r="50" cx="50" cy="50" fill="currentColor" />
  </svg>
</button>

Problem solved. Pack up and call it a day. You've earned a rest.

Except now you want a three buttons—with the same icon—and you know the image is a placeholder—and you want to serve it from a CDN.

In steps the star of the show

SVG has a little-known feature to include & reuse instances of graphical elements. This is achieved with the use tag, which accepts the id of another element by means of the href attribute.

icon.svg

<svg xmlns="http://www.w3.org/2000/svg"
  width="100"
  height="100"
>
  <circle id="roundShape" r="10" cx="10" cy="10" />
  <use href="#roundShape" x="20">
</svg>

But we're not limited to including within the same SVG. We're not even limited to the same file. As long as our external icon SVG has an id, we can include it from wherever we want!*

*(within reason & I haven't tested cross-origin sources)

A working example

So what does all this look like?

icon.svg

<svg xmlns="http://www.w3.org/2000/svg"
  id="iconId"
  width="100"
  height="100"
>
  <circle r="10" cx="10" cy="10" />
</svg>

button.jsx

<button>
  <svg>
    <use href="icon.svg#iconId">
  </svg>
</button>

The href points to a resource, with the URL fragment specifying which element we're including. You must use an id and URL fragment, otherwise it won't work.

Obviously I've cut it down a bit for brevity, but there's not much more to it. Just add your styling code, maybe some :active & :focus pseudo-classes, and you have an externally referenced SVG changing colour.