Editable SVG Icon Systems with Svelte and Heroicons

There are many ways to create an SVG Icon System, but one method that takes advantage of Svelte’s capabilities is to create editable inline icons as components. Some of the advantages of this way of working is:

  • They are easy to edit on the fly
  • You can use standard props and defaults to keep them to a typical size or alter them if you need to
  • They are inline, so no HTTP requests are necessary
  • They can be made accessible dynamically

Base Setup

First, create a folder for all of the icons. Be sure to name them according to some standardized fashion in order to retrieve them easily. For example:

  • components/icons/Pencil.svelte
  • components/icons/PencilAlt.svelte

For more a more detailed example of the entire setup, check out the example repo: https://github.com/babycourageous/svelte-recipes-svg-icons-demo or the Demo Site (https://svelte-recipes-svg-icons-demo.netlify.app/)

The next step is to create a base icon component (Icon.svelte). Start with the markup which will make use of a slot. This will allow the svg parent to accept children (which will be the paths of the icon).

<svg
  xmlns="http://www.w3.org/2000/svg"
  width={width}
  height={height}
  viewBox="0 0 20 20"
  aria-labelledby={name}
  role="presentation"
>
  <title id={name} lang="en">{`${name} icon`}</title>
  <g stroke={color} fill={color}>
    <slot />
  </g>
</svg>

You can use this Icon component as is - the only thing you might need to update is the viewBox depending on the viewBox of the icons you are using. The width, height, color, and name props of the icon will allow the icon to be dynamically updated.

The width, height, and color props will have defaults so that the icon will be rendered consistently unless other values are explicitly passed in. The name prop is used for both the <title> content and its id for accessibility.

Our script will look like this:

<script>
  export let width = 16
  export let height = 16
  export let name = ''
  export let color = 'currentColor'
</script>

The default color prop is set to currentColor so the icon will inherit the color of whatever text surrounds it. Of course, this csn be overridden with a specific color.

Usage

To use it, say we had a Svelte component called PencilAlt.svelte that contained only our icon svg paths like so:

<path
  d="M17.414 2.586a2 2 0 00-2.828 0L7 10.172V13h2.828l7.586-7.586a2 2 0 000-2.828z"
/>
<path
  fill-rule="evenodd"
  d="M2 6a2 2 0 012-2h4a1 1 0 010 2H4v10h10v-4a1 1 0 112 0v4a2 2 0 01-2 2H4a2 2 0 01-2-2V6z"
  clip-rule="evenodd"
/>

We can use the Icon component as follows:

<Icon name="pencil-alt"><PencilAlt /></Icon>

Now, if we’d like to make many sizes for the icon, we can do so very easily:

<p>
  <!-- you can pass in a smaller width and height -->
  <Icon
    width="12"
    height="12"
    name="pencilAlt"
  ><PencilAlt /></Icon>
  <!-- or you can use the default, which is 16 -->
  <Icon name="pencilAlt"><PencilAlt /></Icon>
  <!-- or bump up the size, too -->
  <Icon
    width="30"
    height="30"
    name="pencilAlt"
  ><PencilAlt /></Icon>
</p>

Additional Notes

Some situations require you to recreate or edit every SVG to make global changes. This method can save you that time and hassle. By keeping the logic for the entire icon system in one base component, quick updates can be made and all of your icons will reflect the changes.

When To Avoid This Pattern

This type of SVG icon system is really useful when you have a number of icons that are used in different ways throughout your site. If you’re repeating the same icon many times on one page (e.g. a giant table with a delete icon in each row), it might make more sense to have all of the sprites compiled into a sprite sheet and use tags to load them.