Loading dynamic images in a Vue Component
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.