Randy Allen Headshot

Full Stack Developer | Laravel, Vue, Tailwind

How to insert SVG files directly into your Laravel Vue Components

Posted on July 26th, 2019

Giving Credit

Before we get started I have to say this is 99% based off code written by Jeffrey Way over at Laracasts (specifically: Inline SVGs Using Render Functions).

If you aren't a member of Laracasts, let me tell you, there's no better source for learning Web Development, whether you're a beginner or an expert go signup today!

Let's get started...

Use Case

I had been plugging away on a project for a client for a few months, I knew cleanly getting my SVGs into my Vue Components was going to be an issue but it was something I didn't want to spend time on figuring out right away. So in every component, whenever I needed to use an SVG (i.e. this edit pencil below), I'd copy and paste the HTML from the SVG file into my component...

Imagine if my client wanted to use a different icon for editing? What if they liked this pencil but just wanted a slight change of color? I'd have to go through every component finding each instance of this SVG and manually replace it or update the class name.

I needed a single source of truth for my edit button and I knew I could get that through the use of a Vue Component...

// No more of this every single time I need
// to add my edit button to a component...
<svg class="fill-current text-blue-500 w-4 h-4 cursor-pointer" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M12.3 3.7l4 4L4 20H0v-4L12.3 3.7zm1.4-1.4L16 0l4 4-2.3 2.3-4-4z"/></svg>

// ...wouldn't this be so much better?
<inline-svg name='edit'></inline-svg>

This single source of truth for my edit button would mean if I needed to change it I can change one file and the change would automatically be reflected system-wide!

Let's (really) get started...

Configuring Webpack

By default Webpack will use a file-loader to handle SVG files, this will insert a file path to the SVG in your Vue components. While this could work for us it results in needing additional code in every single Vue Component.

We can avoid any additional code by telling Webpack to handle SVG files with the html-loader.

In his lesson Jeffrey supplies the override you need to put in your webpack.mix.js file, here it is below:

// append to your laravel mix config in webpack.mix.js
.override(config => {
  config.module.rules.find(rule =>
    rule.test.test('.svg')
  ).exclude = /\.svg$/;
      
  config.module.rules.push({
    test: /\.svg$/,
    use: [{ loader: 'html-loader' }]
  })
});

This will exclude SVG files from the file-loader rule and add them to the html-loader rule.

If you're running npm run watch restart the watcher to make sure your changes apply

InlineSvg.js - Download Here

Download the InlineSvg.js file above and place it in your resources/js/components directory of your Laravel application.

This file is only slightly changed from the version Jeffrey made in his lesson. I found that his original file would throw an error if you tried to add multiple classes to your SVG. Additionally you will need to update the following line:

// change path to wherever your SVG files are located
div.innerHTML = require('../../../public/img/svg/' + name + '.svg'); 

Edit app.js

You could import InlineSvg.js into any Vue Component you wanted to on a case-by-case basis but I use it in just about every Vue Component so I made it a global component. You can do this by editing your resources/js/app.js file:

// You have a few options...

// Any version of Laravel...
Vue.component('inline-svg', require('./components/InlineSvg.js').default);

// ...or Laravel 5.7+ with the component auto-registrar
// simply change the regex to auto-register the InlineSvg component 
// from: /\.vue$/i
// to: /\.(vue|js)$/i
const files = require.context('./', true, /\.(vue|js)$/i)

Testing

Let's see if we did everything right...

1) Create edit.svg

Save the following in public/img/svg/edit.svg (or wherever you set your path in InlineSvg.js)

<svg class="fill-current text-blue-500 w-4 h-4 cursor-pointer" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M12.3 3.7l4 4L4 20H0v-4L12.3 3.7zm1.4-1.4L16 0l4 4-2.3 2.3-4-4z"/></svg>

Note: I'm using Tailwind classes in my SVG, if you aren't using Tailwind you'll need to supply your own classes/styling.

2) Utilize our new Vue Component

Add the following to a page on your site:

<inline-svg name="edit"></inline-svg>


3) Run npm run dev

Compile your app and go to the page you put the inline-svg component on and see if it worked!

Conclusion

Hopefully your test was successful. Note that the classes in edit.svg are static but the inline-svg Vue component accepts overrides of classes, width, and height. For example, if we have a one-off requirement to make the edit button bigger and a different color we can simply pass in our own custom Tailwind classes:

<inline-svg name="edit" classes="fill-current text-orange-500 w-10 h-10 cursor-pointer"></inline-svg>

If you aren't using Tailwind you can override the width and height:

<inline-svg name="edit" width="25" height="25"></inline-svg>

Now if you have this edit button on 30 different pages of your app and you need to change it simply open the edit.svg file and make your changes and they will be reflected throughout your entire app :D

If you have any issues or questions please drop me a line and I'll get back to ya.

Randy Allen

Hello world.