Skip to content

Instantly share code, notes, and snippets.

@calebporzio
Created February 19, 2018 19:08
Show Gist options
  • Star 28 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save calebporzio/623c536f7fff77861b8a37ec11a1d3da to your computer and use it in GitHub Desktop.
Save calebporzio/623c536f7fff77861b8a37ec11a1d3da to your computer and use it in GitHub Desktop.
SVG Icon Vue Component
<template>
<div class="inline-block" v-html="require('icon-' + this.icon + '.svg')"></div>
</template>
<style module>
.svg {
fill: currentColor;
height: 1em;
margin-top: -4px;
vertical-align: middle;
width: 1em;
}
</style>
<script>
export default {
props: ['icon'],
mounted() {
this.$el.firstChild.classList.add(this.$style.svg)
this.$el.firstChild.removeAttribute('height')
this.$el.firstChild.removeAttribute('width')
}
}
</script>
// Here is the custom code for my webpack.mix.js file:
// Exclude svg icons
Mix.listen('configReady', function (config) {
const rules = config.module.rules;
const targetRegex = /(\.(png|jpe?g|gif)$|^((?!font).)*\.svg$)/;
for (let rule of rules) {
if (rule.test.toString() == targetRegex.toString()) {
rule.exclude = /\.svg$/;
break;
}
}
});
// Use a custom loader for inline icons
mix.webpackConfig({
module: {
rules: [{
test: /\.svg$/,
use: [{
loader: 'html-loader',
options: {
minimize: true
}
}]
}]
},
resolve: {
modules: [ path.resolve(__dirname, 'resources/assets/svg') ]
}
});
@pryley
Copy link

pryley commented Jan 23, 2020

  1. Install the html-loader package
npm i -D html-loader
  1. Add this to your webpack.mix.js (no other configuration is needed):
mix.webpackConfig({
    resolve: {
        alias: {
            '@': path.resolve(__dirname, 'resources/'),
        },
    },
});
  1. You can now do this:
<template>
    <div v-html="require(`!!html-loader!@/svg/${icon}.svg`)" v-once></div>
</template>

<script>
    export default {
        props: {
            name: {
                default: '',
                type: String,
            },
        },
        mounted() {
            this.$el.firstChild.classList.add(...this.$el.className.split(' '));
            this.$el.outerHTML = this.$el.innerHTML;
        },
    }
</script>

@sebastiaanluca
Copy link

Thanks for all the examples! Based on these, I came up with a mix of some proposals:

npm i -D html-loader

Also ensure you have the path set up correctly in webpack.config.js or just use a relative import.

<template>
    <div
        v-html="require(`!!html-loader!@Icons/${name}.svg`)" class="svg-icon"
        v-once
        role="img"
    ></div>
</template>

<script>
    export default {
        props: {
            name: {
                default: '',
                type: String,
                required: true,
            },
        },

        mounted() {
            this.$el.firstChild.removeAttribute('width')
            this.$el.firstChild.removeAttribute('height')

            // Reassign class list and remove the div wrapper
            this.$el.firstChild.classList.add(...this.$el.className.split(' '))
            this.$el.outerHTML = this.$el.innerHTML
        },
    }
</script>

<style module>
    .svg-icon {
        height: 1em;
        margin-top: -4px;
        vertical-align: middle;
        width: 1em;
    }
</style>

@jerrebm
Copy link

jerrebm commented Apr 16, 2021

Unfortunately, none of the examples worked for me as provided. Here's the solution I ended up with:

  1. Install html-loader:
    npm i -D html-loader

  2. Update webpack.mix:

var path = require('path');

mix.alias({
    '@icons': path.resolve(__dirname, 'resources/assets/svg/'),
});
  1. Create SvgIcon.vue component:
<template>
    <div
        v-html="require(`!!html-loader!@icons/${name}.svg`).default" 
        class="fill-current"
        v-once
    ></div>
</template>

<script>
    export default {
        props: {
            name: {
                default: '',
                type: String,
                required: true,
            },
        },

        mounted() {
            this.$el.firstChild.removeAttribute('width')
            this.$el.firstChild.removeAttribute('height')

            // Reassign class list and remove the div wrapper
            this.$el.firstChild.classList.add(...this.$el.className.split(' '))
            this.$el.outerHTML = this.$el.innerHTML
        },
    }
</script>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment