SVG `symbol` a Good Choice for Icons

Chris Coyier //

You could design an icon set where the icons all had the exact same aspect ratio. But that's probably not typically going to be the case. The container around a little beaker icon might be tall and narrow. The container around a little fish perhaps short and long. You probably shouldn't have to think too much about that, but unfortunately you kinda have to when you use an SVG icon system as I've described in the past, because you need to use the viewBox attribute to describe that container/aspect ratio.

An improvement is to use the <symbol> element in SVG instead of directly referencing shapes (or a <g>), because you can define the viewBox directly on the <symbol> and then not need one when you <use> it later in an <svg>.

An example is in order.

Here's two icons with very different aspect ratios, as you can tell from the artboard in Illustrator.

We could adjust them to all be placed within a consistent aspect ratio, but I find it's more flexible and workable to know that your icons edges are right where the shapes stop, not with an arbitrary amount of white space around them.

The "Old" Way

If we go the <defs>-block route, we might combine them into:

<svg>
  <defs>
    <g id="shape-icon-1">
      <!-- all the paths and shapes and whatnot for this icon -->
    <g>
    <g id="shape-icon-2">
      <!-- all the paths and shapes and whatnot for this icon -->
    <g>
  </defs>
</svg>

Then use them like:

<!-- These viewBox's better be right or the icons won't look right! -->

<svg class="icon" viewBox="214.7 0 182.6 792">
  <use xlink:href="#shape-icon-1" />
</svg>

<svg class="icon" viewBox="0 26 100 48">
  <use xlink:href="#shape-icon-2" />
</svg>

That puts a good amount of onus on the implementer to get those viewBox attributes correct in the markup. That's one reason why might want to try and get all those icons at a consistent viewBox="0 0 100 100" (or something), but then we have that kinda arbitrary whitespace thing going on.

The "New" Way

Enter Fabrice Weinberg and TxHawks. Fabrice works on grunt-svgstore, a Grunt plugin for creating the SVG sprites from a folder of SVG files. This kind of thing makes the SVG icon workflow quick and easy. That is, except for the fact that you need to know that dang viewBox for each icon before you use it.

TxHawks suggested having grunt-svgstore at least put data-* attributes on the <g> elements that wrap each icon, so there could be programmatic access to what it is supposed to be. But unfortunately SVG doesn't allow those (it would have probably worked, but might as well make a build tool spec-compliant). It doesn't matter though, because soon after, they suggested using <symbol> instead, which turns out to be quite a good idea.

Instead of using <g> to wrap all the icon shapes, use <symbol>, like this:

<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
  
  <symbol id="beaker" viewBox="214.7 0 182.6 792">
    <!-- <path>s and whatever other shapes in here -->  
  </symbol>
  
  <symbol id="shape-icon-2" viewBox="0 26 100 48">
    <!-- <path>s and whatever other shapes in here -->  
  </symbol>
  
</svg>

Note that the viewBox is defined for each icon and as you're defining it as opposed to when you're using it. That means using it becomes easier:

<!-- We ain't even need no viewBox round here. --> 

<svg class="icon">
  <use xlink:href="#shape-icon-1" />
</svg>

<svg class="icon">
  <use xlink:href="#shape-icon-2" />
</svg>

Easier, and less error prone.

And it gets better: you can add <title> and <desc> tags in the symbol as well, meaning the accessibility stuff is baked in as you use it.

<symbol id="icon1" viewBox="original-file's-viewBox">
  <title>original-file's-title</title> 
  <desc>original-file's-desc</desc>

  <!-- <path>s and other shapes -->

</symbol>

grunt-svgstore does this now, thanks to TxHawks and Fabrice!

Why <symbol> is better for icons

Just to put a point on it:

  1. The viewBox can be defined on the symbol, so you don't need to use it in the markup (easier and less error prone).
  2. title and desc tags can be added there as well, making accessibility easier to do right.
  3. Symbols don't display as you define them, so no need for a <defs> block.
  4. This is probably what <symbol> was invented for anyway.

Demo

It works:

See the Pen Hwcxp by Chris Coyier (@chriscoyier) on CodePen.


Wanna learn more about SVG?

I have a full course available called Everything You Need to Know about SVG that covers the whole spectrum of SVG from the perspective of a web designer and front end developer.