Webpack: Image module request as dynamic property of img tag

Created on 16 May 2016  路  24Comments  路  Source: vuejs-templates/webpack

I am very new to vueJs and still exploring its capabilities, I am having trouble wrapping my head around the following:

This image reference works but the problem is that the country has to be dynamic

<img src="~assets/spain.jpg">

Dynamic:

<img :src="imagePath">

computed: { imagePath: function () { return '~assets/' + this.country.name + '.jpg' } }

screen shot 2016-05-16 at 23 27 35

What am I doing wrong?

NOTE: I know that I can fix it by doing the following:

imagePath: function () { return '/static/' + this.country.name + '.jpg' }

Most helpful comment

imagePath: function () {
  return require('~assets/' + this.country.name + '.jpg')
}

All 24 comments

Because the imagePath is only rendered by Vue at runtime, Webpack has no chance of rewriting it.

In your JS, use require('~assets/' + this.country.name + '.jpg') instead. This allows Webpack to return the correct static path.

And note, that require('~assets/' + this.country.name + '.jpg') will take all image _names_ (or maybe all file _names_) and keep it in build files. So, if you have ten thousand images in ~assets/, be ready for this.

I am not sure where to place this require?

imagePath: function () {
  return require('~assets/' + this.country.name + '.jpg')
}

This gives me:

Module not found: Error: Cannot resolve module '~assets' in /Users/jdruwe/Personal/VueJs/travel-vue/src/components

screen shot 2016-05-18 at 15 56 51

I can not use dynamic image path in template.
Does anyone have any suggestion for this problem?

The problem looks like, image names(paths?) are translated to base64 name and it refer to "translated path" if I use static image path.
However, I was using dynamic image path, that does not refer to translated image path.

Here is what template-compiler did

----------------- BEFORE -------------------
{ type: 1,
  tag: 'img',
  attrsList: [ { name: ':src', value: 'item.src' } ],
  attrsMap:
   { ':src': 'item.src',
     ':class': '[item.isActive ? \'active\' : \'non-active\']' },
  parent:
   { type: 1,
     tag: 'a',
     attrsList: [ [Object] ],
     attrsMap: { ':href': 'item.href' },
     parent:
      { type: 1,
        tag: 'li',
        attrsList: [],
        attrsMap: [Object],
        parent: [Object],
        children: [Object],
        for: 'list',
        alias: 'item',
        plain: true },
     children: [ [Circular] ],
     plain: false,
     hasBindings: true,
     attrs: [ [Object] ] },
  children: [],
  plain: false,
  classBinding: '[item.isActive ? \'active\' : \'non-active\']',
  hasBindings: true,
  attrs: [ { name: 'src', value: 'item.src' } ] }
{ name: 'src', value: 'item.src' } 'src'
----------------- AFTER -------------------
{ type: 1,
  tag: 'img',
  attrsList: [ { name: ':src', value: 'item.src' } ],
  attrsMap:
   { ':src': 'item.src',
     ':class': '[item.isActive ? \'active\' : \'non-active\']' },
  parent:
   { type: 1,
     tag: 'a',
     attrsList: [ [Object] ],
     attrsMap: { ':href': 'item.href' },
     parent:
      { type: 1,
        tag: 'li',
        attrsList: [],
        attrsMap: [Object],
        parent: [Object],
        children: [Object],
        for: 'list',
        alias: 'item',
        plain: true },
     children: [ [Circular] ],
     plain: false,
     hasBindings: true,
     attrs: [ [Object] ] },
  children: [],
  plain: false,
  classBinding: '[item.isActive ? \'active\' : \'non-active\']',
  hasBindings: true,
  attrs: [ { name: 'src', value: 'item.src' } ] }

This code above is created by adding console.log(el) up and down on the line 20
https://github.com/vuejs/vue-loader/blob/92d97a60e84ef5b96ea27aba71e44953ca369551/lib/template-compiler.js#L12-L28

Usually, if BEFORE has the static path, AFTER has require statement.
However, if BEFORE has variables it shouldn't.

The reason why dynamic path can not be treated by url-loader is variables might be expanded after compile the code.

Here is my component just FYI

<template>
  <div id="test-menu">
    <ul class="menu">
      <li v-for="item in list">
        <a :href="item.href">
          <img :src="item.src" :class="[item.isActive ? 'active' : 'non-active']">
        </a>
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  name: "test-menu",
  props: {
    list: {
      type: Array,
      default: () => {
        return [{
          href: "hello",
          src: "world",
          isActive: true,
        }];
      },
    },
  },
}
</script>

I'm using slot to avoid this problem for now

In script , require('imgpath') not require the ~

my solution!

1.<img :src="icon_src" />
2.import the image at the top level
import icon from 'icon.png';

  1. data () { return { icon_src:icon } }
    or computed instead

this is still broken. I tried both putting the required way in a function and computed property and tried importing from top level and all don't work.

The usage of ~ will depend on how your webpack aliases are setup. For example I'm using the vue-cli with the webpack template, which uses @ to resolve the src directory (I think the webpack-simple uses ~). So my solution for this the above was to use require('@/assets/' + this.country.name + '.jpg')

I have the same problem... trying to follow you all but this does not work:

<template>
  <div class="item">
    <h1>{{ item.name }}</h1>
    <img
      v-for="icon in item.icons"
      :key="icon"
      :src="path(icon)"
     />
  </div>
</template>

<script>
export default {
  name: 'DataItem',
  props: ['item'],
  computed: {
    path (icon) {
      return require('../assets/icons/' + icon + '.svg')
    }
  }
}
</script>

Also, should the icon images themselves be separate components? They are quite small and each Item has several.

This is what the working images would look like. When I hardcoded these webpack included them as base64 because they are all simple SVG:

    <img src="../assets/icons/phone.svg" />
    <img src="../assets/icons/usb.svg" />
    <img src="../assets/icons/wifi.svg" />

Thanks for any help.

@makeitTim Try putting the path function inside methods instead of computed, it should fix your issue.

with vue-cli none of them works

<img :src="buildLogoUrl(logo)" :alt="buildAltTag(logo)">

and

methods: {
    buildLogoUrl(name) {
      return require(`../assets/images/logo_${name}.png`);
    },
    buildAltTag(name) {
      return `Logo of ${name}`;
    }
  }

works with vue-cli 3.1.1

My code
<img alt class="img-circle img-responsive" :src="getImage(imageURL)" >

and

export default {
    name: 'timelineItem',
    props: ['heading','subheading','content','imageURL','contentPosition'],
    methods: {
        getImage(imageURL){
            return require(imageURL);
        }
    }
}

and using vue 2.6.8 and vue/cli 3.5.0

This returns Error: Cannot find module '~@/assets/img/timeline-4.jpg'"

I've tried a lot of combinations from different threads in place of '~@/assets/img/timeline-4.jpg'

My code
<img alt class="img-circle img-responsive" :src="getImage(imageURL)" >

and

export default {
    name: 'timelineItem',
    props: ['heading','subheading','content','imageURL','contentPosition'],
    methods: {
        getImage(imageURL){
            return require(imageURL);
        }
    }
}

and using vue 2.6.8 and vue/cli 3.5.0

This returns Error: Cannot find module '~@/assets/img/timeline-4.jpg'"

I've tried a lot of combinations from different threads in place of '~@/assets/img/timeline-4.jpg'

Yup same problem, kind of sad really that this is an issue when you're trying to iterate over creating a simple list of icon navigation

@yyx990803 can you please help on this issue? Why is referencing an image dymanically so hard in the latest vue-cli?

Hi guys I had to use backticks to make it work dynamically:

<img :src="require(`@/assets/images/projects/` + project.image)" width="100px" />

Hi guys I had to use backticks to make it work dynamically:

<img :src="require(`@/assets/images/projects/` + project.image)" width="100px" />

Will give this a try thanks man!

Hi, I have another issue now:

userEdit() {
      //uploads the image
      if (this.formImageFilename.name) {
        let formImageData = new FormData()
        formImageData.append('file', this.formImageFilename)
        axios.post('/db/userimage', formImageData, { headers: { 'Content-Type': 'multipart/form-data' } })
        // once it has uploaded the new image, it deletes the old one 
        .then(res=>{console.log(res), this.deleteOldImage})
        .catch(err=>{console.log(err)})
      }else{
        this.userUpdate() //if no new image has to be inserted, it proceeds to update the user information
      }
    }

this function is called when I submit a form to update the user. In my navbar the user has its image:

<img v-if="this.$auth.user.image" class="userlogo m-2 rounded-circle" 
:src="require('../assets/images/users/' + this.$auth.user.image)"> 

but when the image is uploaded and nodemon recompiles the changes because of the new image uploaded, it takes some time to have the image path rendered, and without a setTimeout() my :src="require() cannot find "the module", i.e. the image.

Do you guys know any workaround on this?
Am I doing a wrong process? Is it wrong that nodemon recompiles? I tried to avoid it but the image is not refreshed..
Is there a possibility to relink the image, use mounted()/next tick, get a function that awaits for the recompile ..?

A good solution for me is use it with import

import imgPath from '@/assets/imgPath'

Next in data or in method I return the img source like this

data() { return { imgPath } }

Hi guys I had to use backticks to make it work dynamically:

<img :src="require(`@/assets/images/projects/` + project.image)" width="100px" />

After looking all day and trying a lot of ways using computed(), modules() and so on yours was the only way to make it work (Nuxt + nuxtjs/svg )
Thanks !

This no longer seems to work.

The usage of ~ will depend on how your webpack aliases are setup. For example I'm using the vue-cli with the webpack template, which uses @ to resolve the src directory (I think the webpack-simple uses ~). So my solution for this the above was to use require('@/assets/' + this.country.name + '.jpg')

its work thank you

Was this page helpful?
0 / 5 - 0 ratings