When I first started using Vue, one thing I got continually wrong was how to load an image into a Vue component dynamically. At first, I found that using an absolute URL worked, but that was only useful if I was storing the images on a CDN or other external site. If I included the images in my project, as either lightweight icons or static images, then using an absolute URL, with hostname and all, didn’t really work. What about when I wanted to test some new images locally or on a dev server? Linking directly to the images in production just wasn’t going to cut it.

When researching this, the Vue CLI documentation for static assets was, frankly, a little less than helpful. They didn’t give a good example for what I was trying to do.

What I was building was a little form control to take credit card numbers. It consisted of a single file Vue component and looked something like this:

<template>
	<div class="form-group">
    <label>Credit Card Number</label>
    <input type="text" name="creditCardNumber" v-model="creditCardNumber"
           placeholder="1111111111111111">
    <img class="creditLogo" v-bind:src="creditCardLogoSrc">
  </div>
</template>

<script>
export default {
  name: 'credit-card-field',
  computed: {
    creditCardLogoSrc(vm) {
      if(vm.creditCardNumber.startsWith('4')) {
        // Return the Visa image
      } else if (vm.creditCardNumber.startsWith('5')) {
        // Return the Mastercard image
      } else if (vm.creditCardNumber.startsWith('6')) {
        // Return the Discover image
      } else {
        // Return the generic image
      }
    }
  },
  data() {
    return {
      creditCardNumber: ''
    }
  }
}
</script>

What I wanted to happen was, as the user is typing in their credit card number, I will look at it and swap out the img tag’s source to show the credit card type that they were entering. I had a Visa image, a Mastercard image and a Discover image as well as the image of a generic credit card if it didn’t match any of those. Since all Visas start with 4, all Mastercards start with 5, and all Discover cards start with 6, this would be a pretty easy check to do.

The logic ended up being the easy part. The hard part was loading the images. I had the images in the Vue CLI provided assets folder, but how do I load them in?

Looking at the documentation, there are a lot of “in templates, do this” and “only in templates!” kinds of things. But I wasn’t in a template. I was in the code part of my component.

After much research, I found the answer was to require() the images from the asset folder. Intuitive! 😒

So, when I need to use an image that is in the assets folder, I can require() the relative path to that image in my computed method:

creditCardLogoSrc(vm) {
  if(vm.creditCardNumber.startsWith('4')) {
    return require('../assets/visa.png');
  } else if (vm.creditCardNumber.startsWith('5')) {
    return require('../assets/mastercard.png');
  } else if (vm.creditCardNumber.startsWith('6')) {
    return require('../assets/discover.png');
  } else {
    return require('../assets/credit.png');
  }
}

One nice plus to doing it this way is, if the image is small enough, require() will return a dataurl instead of a URL path, which will save an extra call to the server and make the component a little more self contained.

So if you need to load images from inside a Vue CLI project’s assets folder from outside the template of your component, now you know how.