Inline SVG's with Vue, Typescript, and TailwindCSS

Published by Alex Oxthorn on March 31, 2019

Until recently, I'd been using a sprite sheet of SVG symbols and a component that dynamically inserted an icon using the symbol id via the HTML <use> tag. I grew dissatisfied with this approach for a couple of reasons: first, it required the user to download the entire sprite sheet, regardless of what icons were used, resulting in an additional browser request and a bloated download. Second, If I need to add additional icons, I would have to bust the cache of the old icon file, forcing users to re-download the entire sprite sheet for a very minor change. Third, since the <use> tag creates a closed shadow dom element, changing the icons fill and stroke values with CSS is not possible.

So, the new method would have to satisfy these conditions: Icons would need to live in separate .svg files that could be individually cached (each SVG file would also naturally be much smaller this way) and the component that imported them would need to inline the SVG markup so that CSS could be applied to each icon as needed.

Icon.vue

<template>
  <div class="pointer-events-none" v-html="require(`@/assets/svg/${name}.svg`)"></div>
</template>

<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
@Component({})
export default class Icon extends Vue {
  @Prop() name!: string;
}
</script>

vue.config.js

  chainWebpack: config => {
    const svgRule = config.module.rule('svg');

    // clear all existing loaders.
    // if you don't do this, the loader below will be appended to
    // existing loaders of the rule.
    svgRule.uses.clear();
    svgRule
      .use('html-loader')
      .loader('html-loader');
  },
Typescript, Vue, TailwindCSS