Embedding SVG's as an icon set

Embedding SVG's as an icon set

SVG's are pretty amazing. No matter the size, they tend to always look crisp and have lots of options to customize and override. They are great for icons and logo's. The reason for this crispness is that SVG are nothing more but a collection of points in a grid that can be redrawn on demand instead of fixed pixels in a certain grid size.

When it comes to using SVG's on a website there are couple options:

  • Use an img tag with an SVG file as the source
  • Use the SVG as a background image
  • Inline the SVG

These are all great options, but either lack styling options or don't scale well on bigger projects. For example using an SVG as an image or background image does not let you change any of the SVG attributes such as fill or stroke. When adding the SVG inline you can style it, but it doesn't scale if you want to reuse it in other places. If you're using a framework like react you'll end up with a component for each SVG. Whenever you'll want to make a change to the way an icon is rendered, you'll have to replicate that same change for each component.

Another way

Another way is to embed multiple SVG's into a single file full of definitions. Here's an example of what that looks like:

<svg
      aria-hidden="true"
      style="position: absolute; width: 0; height: 0; overflow: hidden;"
      version="1.1"
      xmlns="http://www.w3.org/2000/svg"
    >
      <defs>
            <symbol id="icon-add" viewBox="0 0 20 20">
                <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
                   <path d="M9.5 3V17"  stroke-width="1.6" stroke-linecap="round"/>
                   <path d="M16.5 10H2.5"  stroke-width="1.6" stroke-linecap="round"/>
                </svg>
            </symbol>

            <symbol id="icon-back" viewBox="0 0 20 20">
                <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
                    <path d="M17 10L3 10"  stroke-width="1.6" stroke-linecap="round"/>
                    <path d="M9.5 17L2.5 10L9.5 3"  stroke-width="1.6" stroke-linecap="round"/>
                </svg>
            </symbol>
     </defs>
</svg>

What you can see in the code above is that we have an SVG that is invisible (even to screen readers) that has a definition element with one or more symbols in it, that contain an SVG each. As a result we now have a list of SVG's that we can access, invisible to the user when this is included in the html somewhere.

Now that we have list of SVG's that the browser is aware of, the next step is to access the relevant items inside. We can do this by using the use tag:

<use href='#icon-add`/>

use takes a node from the svg definitions and copies that inside use tag. The great this about this is that we are inlining SVG's without manually duplicating code as its now coming from a single source. On top of that, because the SVG is essentially inlined, we can style this as we would an inline SVG by wrapping the use tag with svg and it will apply it to the SVG embedded inside like so:

<svg
  width=50
  height=50
  viewBox="0 0 24 24"
  stroke="#00000"
>
    <use href='#icon-add`/>
</svg>

Another great thing about using use is the browser support, as its supported by all major browsers

Make sure to embed the file containing all the SVG's so the browser knows where to find the reference. Otherwise this won't work.

Code Examples

Here's an example of using React when creating an Icon component:

const Icon = ({ size, name, color, fill }) => {
  return (
    <svg
      width={size}
      height={size}
      viewBox="0 0 24 24"
      stroke={color}
      fill={fill}
      {...props}
    >
      <use href={`#icon-${name}`} />
    </svg>
  );
};

EmberJS

<svg width={{@size}} height={{@size}} viewBox="0 0 24 24" ...attributes>
    <use href={{concat "#icon-" @name}}></use>
</svg>