DEV Community

Cover image for Loading dynamic images in a Vue Component
Joe Erickson
Joe Erickson

Posted on • Originally published at jerickson.net

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>
Enter fullscreen mode Exit fullscreen mode

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');
  }
}
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (2)

Collapse
 
webdevchallenges profile image
Marc

Can't you just return the absolute path without FQDN like /assets/visa.png instead?

Collapse
 
firstclown profile image
Joe Erickson • Edited

Because the image won't live in /assets/visa.png! Everything in the assets folder, if it is used, will be deployed to an img/ directory if it's not turned into a dataurl.

Because it lives in the src/ directory in the project, it's not accessible once the project is built for production. Using require() lets the Vue CLI (actually webpack) know that you're using it and is then deployed properly during the build process. You can read more about that at Static Assets Handling in the Vue CLI documentation.