Vue 3 Vite – dynamic image src

I’m using Vue 3 with Vite. And I have a problem with dynamic img src after Vite build for production. For static img src there’s no problem.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code><img src="/src/assets/images/my-image.png" alt="Image" class="logo"/>
</code>
<code><img src="/src/assets/images/my-image.png" alt="Image" class="logo"/> </code>
<img src="/src/assets/images/my-image.png" alt="Image" class="logo"/>

It works well in both cases: when running in dev mode and after vite build as well. But I have some image names stored in database loaded dynamically (Menu icons). In that case I have to compose the path like this:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code><img :src="'/src/assets/images/' + menuItem.iconSource" />
</code>
<code><img :src="'/src/assets/images/' + menuItem.iconSource" /> </code>
<img :src="'/src/assets/images/' + menuItem.iconSource" />

(menuItem.iconSource contains the name of the image like “my-image.png”).
In this case it works when running the app in development mode, but not after production build. When Vite builds the app for the production the paths are changed (all assests are put into _assets folder). Static image sources are processed by Vite build and the paths are changed accordingly but it’s not the case for the composed image sources. It simply takes /src/assets/images/ as a constant and doesn’t change it (I can see it in network monitor when app throws 404 not found for image /src/assets/images/my-image.png).
I tried to find the solution, someone suggests using require() but I’m not sure vite can make use of it.

Update 2022: Vite 3.0.9 + Vue 3.2.38

Solutions for dynamic src binding:

1. With static URL

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code><script setup>
import imageUrl from '@/assets/images/logo.svg' // => or relative path
</script>
<template>
<img :src="imageUrl" alt="img" />
</template>
</code>
<code><script setup> import imageUrl from '@/assets/images/logo.svg' // => or relative path </script> <template> <img :src="imageUrl" alt="img" /> </template> </code>
<script setup>
import imageUrl from '@/assets/images/logo.svg' // => or relative path
</script>

<template>
  <img :src="imageUrl" alt="img" />
</template>

2. With dynamic URL & relative path

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code><script setup>
const imageUrl = new URL(`./dir/${name}.png`, import.meta.url).href
</script>
<template>
<img :src="imageUrl" alt="img" />
</template>
</code>
<code><script setup> const imageUrl = new URL(`./dir/${name}.png`, import.meta.url).href </script> <template> <img :src="imageUrl" alt="img" /> </template> </code>
<script setup>
const imageUrl = new URL(`./dir/${name}.png`, import.meta.url).href
</script>

<template>
  <img :src="imageUrl" alt="img" />
</template>

3. With dynamic URL & absolute path, computed

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code><script setup>
import { computed } from 'vue'
const props = defineProps({
name: String,
})
const imageUrl = computed(
() => new URL(`@/assets/images/${props.name}.png`, import.meta.url).href
);
</script>
<template>
<img :src="imageUrl" />
</template>
</code>
<code><script setup> import { computed } from 'vue' const props = defineProps({ name: String, }) const imageUrl = computed( () => new URL(`@/assets/images/${props.name}.png`, import.meta.url).href ); </script> <template> <img :src="imageUrl" /> </template> </code>
<script setup>
import { computed } from 'vue'

const props = defineProps({
    name: String,
})

const imageUrl = computed(
    () => new URL(`@/assets/images/${props.name}.png`, import.meta.url).href
);
</script>

<template>
  <img :src="imageUrl" />
</template>

4. With dynamic URL & absolute path

Due to Rollup Limitations, all imports must start relative to the importing file and should not start with a variable.

You have to replace the alias @/ with /src

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code><script setup>
const imageUrl = new URL('/src/assets/images/logo.svg', import.meta.url)
</script>
<template>
<img :src="imageUrl" alt="img" />
</template>
</code>
<code><script setup> const imageUrl = new URL('/src/assets/images/logo.svg', import.meta.url) </script> <template> <img :src="imageUrl" alt="img" /> </template> </code>
<script setup>
const imageUrl = new URL('/src/assets/images/logo.svg', import.meta.url)
</script>

<template>
  <img :src="imageUrl" alt="img" />
</template>


2022 answer: Vite 2.8.6 + Vue 3.2.31

Here is what worked for me for local and production build:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code><script setup>
const imageUrl = new URL('./logo.png', import.meta.url).href
</script>
<template>
<img :src="imageUrl" />
</template>
</code>
<code><script setup> const imageUrl = new URL('./logo.png', import.meta.url).href </script> <template> <img :src="imageUrl" /> </template> </code>
<script setup>
const imageUrl = new URL('./logo.png', import.meta.url).href
</script>

<template>
<img :src="imageUrl" />
</template>

Note that it doesn’t work with SSR


Vite docs: new URL

10

Following the Vite documentation you can use the solution mentioned and explained here:

vite documentation

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>const imgUrl = new URL('./img.png', import.meta.url)
document.getElementById('hero-img').src = imgUrl
</code>
<code>const imgUrl = new URL('./img.png', import.meta.url) document.getElementById('hero-img').src = imgUrl </code>
const imgUrl = new URL('./img.png', import.meta.url)

document.getElementById('hero-img').src = imgUrl

I’m using it in a computed property setting the paths dynamically like:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code> var imagePath = computed(() => {
switch (condition.value) {
case 1:
const imgUrl = new URL('../assets/1.jpg',
import.meta.url)
return imgUrl
break;
case 2:
const imgUrl2 = new URL('../assets/2.jpg',
import.meta.url)
return imgUrl2
break;
case 3:
const imgUrl3 = new URL('../assets/3.jpg',
import.meta.url)
return imgUrl3
break;
}
});
</code>
<code> var imagePath = computed(() => { switch (condition.value) { case 1: const imgUrl = new URL('../assets/1.jpg', import.meta.url) return imgUrl break; case 2: const imgUrl2 = new URL('../assets/2.jpg', import.meta.url) return imgUrl2 break; case 3: const imgUrl3 = new URL('../assets/3.jpg', import.meta.url) return imgUrl3 break; } }); </code>
    var imagePath = computed(() => {
      switch (condition.value) {
        case 1:
          const imgUrl = new URL('../assets/1.jpg',
            import.meta.url)
          return imgUrl
          break;
        case 2:
          const imgUrl2 = new URL('../assets/2.jpg',
            import.meta.url)
          return imgUrl2
          break;
        case 3:
          const imgUrl3 = new URL('../assets/3.jpg',
            import.meta.url)
          return imgUrl3
          break;
      }
    });

Works perfectly for me.

The simplest solution I’ve found for this is to put your images in the public folder located in your directory’s root.

You can, for example, create an images folder inside the public folder, and then bind your images dynamically like this:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code><template>
<img src:="`/images/${ dynamicImageName }.jpeg`"/>
</template>
</code>
<code><template> <img src:="`/images/${ dynamicImageName }.jpeg`"/> </template> </code>
<template>
  <img src:="`/images/${ dynamicImageName }.jpeg`"/>
</template>

Now your images should load correctly in both development and production.

3

According to the docs here. For dynamic images, it’s best to do something like this…

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>/* create a util function or method or computed property
also assuming your static images are in assets folder
*/
const getImageUrl = (path: string) => {
return new URL(`../assets/${path}`, import.meta.url).href;
};
// and use in code like this assuming products is a list
<ul>
<li v-for="product in products" :key="product.id">
<img alt="nice image" :src="getImageUrl(product.img)" />
</li>
</ul>
</code>
<code>/* create a util function or method or computed property also assuming your static images are in assets folder */ const getImageUrl = (path: string) => { return new URL(`../assets/${path}`, import.meta.url).href; }; // and use in code like this assuming products is a list <ul> <li v-for="product in products" :key="product.id"> <img alt="nice image" :src="getImageUrl(product.img)" /> </li> </ul> </code>
/* create a util function or method or computed property
   also assuming your static images are in assets folder
*/
const getImageUrl = (path: string) => {
 return new URL(`../assets/${path}`, import.meta.url).href;
};

// and use in code like this assuming products is a list
<ul>
  <li v-for="product in products" :key="product.id">
   <img alt="nice image" :src="getImageUrl(product.img)" />
  </li>
</ul>

This works for me perfectly and I hope it helps someone.

1

All you need is to just create a function which allows you to generate a url.

from vite documentation static asset handling

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>const getImgUrl = (imageNameWithExtension)=> new URL(`./assets/${imageNameWithExtension}`, import.meta.url).href;
</code>
<code>const getImgUrl = (imageNameWithExtension)=> new URL(`./assets/${imageNameWithExtension}`, import.meta.url).href; </code>
const getImgUrl = (imageNameWithExtension)=> new URL(`./assets/${imageNameWithExtension}`, import.meta.url).href;

//use

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code><img :src="getImgUrl(image)" alt="...">
</code>
<code><img :src="getImgUrl(image)" alt="..."> </code>
<img :src="getImgUrl(image)" alt="...">

Use import.meta.glob for absolute paths with variables

For absolute paths, there is a way to load images dynamically. It even supports the @ alias.

Try import.meta.glob

Heres a little convenience method for getting a file.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>function getImage(fileName) {
try {
const modules = import.meta.glob('@/assets/images/**/*.{png,svg}', { eager: true, import: 'default' })
const moduleKeys = Object.keys(modules)
const fileSrc = moduleKeys.find(key => key.endsWith(fileName))
return fileSrc ? modules[fileSrc] : ''
} catch (err) {
console.log(err)
}
}
</code>
<code>function getImage(fileName) { try { const modules = import.meta.glob('@/assets/images/**/*.{png,svg}', { eager: true, import: 'default' }) const moduleKeys = Object.keys(modules) const fileSrc = moduleKeys.find(key => key.endsWith(fileName)) return fileSrc ? modules[fileSrc] : '' } catch (err) { console.log(err) } } </code>
function getImage(fileName) {
  try {
    const modules = import.meta.glob('@/assets/images/**/*.{png,svg}', { eager: true, import: 'default' })
    const moduleKeys = Object.keys(modules)
    const fileSrc = moduleKeys.find(key => key.endsWith(fileName))

    return fileSrc ? modules[fileSrc] : ''
  } catch (err) {
    console.log(err)
  }
}
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code><img :src="getImage('whatever.png')" />
</code>
<code><img :src="getImage('whatever.png')" /> </code>
<img :src="getImage('whatever.png')" />

1

first, check the image directory src/assets/images/ then use import.meta.glob

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code><template>
<div>
<img :src="imageSrc" alt="Menu Icon" />
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue';
const menuItem = ref({
iconSource: 'my-image.png'
});
// Use import.meta.glob to load all images from the directory
const images = import.meta.glob('/src/assets/images/*', { eager: true });
const imageSrc = computed(() => {
const imagePath = `/src/assets/images/${menuItem.value.iconSource}`;
if (images[imagePath]) {
return images[imagePath].default;
} else {
return '/src/assets/images/default.png';
}
});
</script>
</code>
<code><template> <div> <img :src="imageSrc" alt="Menu Icon" /> </div> </template> <script setup lang="ts"> import { ref, computed } from 'vue'; const menuItem = ref({ iconSource: 'my-image.png' }); // Use import.meta.glob to load all images from the directory const images = import.meta.glob('/src/assets/images/*', { eager: true }); const imageSrc = computed(() => { const imagePath = `/src/assets/images/${menuItem.value.iconSource}`; if (images[imagePath]) { return images[imagePath].default; } else { return '/src/assets/images/default.png'; } }); </script> </code>
<template>
  <div>
    <img :src="imageSrc" alt="Menu Icon" />
  </div>
</template>

<script setup lang="ts">
import { ref, computed } from 'vue';

const menuItem = ref({
  iconSource: 'my-image.png'
});

// Use import.meta.glob to load all images from the directory
const images = import.meta.glob('/src/assets/images/*', { eager: true });

const imageSrc = computed(() => {
  const imagePath = `/src/assets/images/${menuItem.value.iconSource}`;
  if (images[imagePath]) {
    return images[imagePath].default;
  } else {
    return '/src/assets/images/default.png';
  }
});
</script>

Please try the following methods

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>const getSrc = (name) => {
const path = `/static/icon/${name}.svg`;
const modules = import.meta.globEager("/static/icon/*.svg");
return modules[path].default;
};
</code>
<code>const getSrc = (name) => { const path = `/static/icon/${name}.svg`; const modules = import.meta.globEager("/static/icon/*.svg"); return modules[path].default; }; </code>
const getSrc = (name) => {
    const path = `/static/icon/${name}.svg`;
    const modules = import.meta.globEager("/static/icon/*.svg");
    return modules[path].default;
  };

3

In the context of [email protected], you can use new URL(url, import.meta.url) to construct dynamic paths. This pattern also supports dynamic URLs via template literals.
For example:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code><img :src="`/src/assets/images/${menuItem.iconSource}`" />
</code>
<code><img :src="`/src/assets/images/${menuItem.iconSource}`" /> </code>
<img :src="`/src/assets/images/${menuItem.iconSource}`" />

However you need to make sure your build.target support import.meta.url. According to Vite documentation, import.meta is a es2020 feature but [email protected] use es2019 as default target. You need to set esbuild target in your vite.config.js:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code> // vite.config.js
export default defineConfig({
// ...other configs
optimizeDeps: {
esbuildOptions: {
target: 'es2020'
}
},
build: {
target: 'es2020'
}
})
</code>
<code> // vite.config.js export default defineConfig({ // ...other configs optimizeDeps: { esbuildOptions: { target: 'es2020' } }, build: { target: 'es2020' } }) </code>
  // vite.config.js
  export default defineConfig({
    // ...other configs

    optimizeDeps: {
      esbuildOptions: {
        target: 'es2020'
      }
    },

    build: {
      target: 'es2020'
    }
  })

My enviroment:

  • vite v2.9.13
  • vue3 v3.2.37

In vite.config.js, assign @assets to src/assets

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>'@assets': resolve(__dirname, 'src/assets')
</code>
<code>'@assets': resolve(__dirname, 'src/assets') </code>
'@assets': resolve(__dirname, 'src/assets')

Example codes:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code><template>
<div class="hstack gap-3 mx-auto">
<div class="form-check border" v-for="p in options" :key="p">
<div class="vstack gap-1">
<input class="form-check-input" type="radio" name="example" v-model="selected">
<img :src="imgUrl(p)" width="53" height="53" alt="">
</div>
</div>
</div>
</template>
<script>
import s1_0 from "@assets/pic1_sel.png";
import s1_1 from "@assets/pic1_normal.png";
import s2_0 from "@assets/pic2_sel.png";
import s2_1 from "@assets/pic2_normal.png";
import s3_0 from "@assets/pic3_sel.png";
import s3_1 from "@assets/pic3_normal.png";
export default {
props: {
'options': {
type: Object,
default: [1, 2, 3, 4]
}
},
data() {
return {
selected: null
}
},
methods: {
isSelected(val) {
return val === this.selected;
},
imgUrl(val) {
let isSel = this.isSelected(val);
switch(val) {
case 1:
case 2:
return (isSel ? s1_0 : s1_1);
case 3:
case 4:
return (isSel ? s2_0 : s2_1);
default:
return (isSel ? s3_0 : s3_1);
}
}
}
}
</script>
</code>
<code><template> <div class="hstack gap-3 mx-auto"> <div class="form-check border" v-for="p in options" :key="p"> <div class="vstack gap-1"> <input class="form-check-input" type="radio" name="example" v-model="selected"> <img :src="imgUrl(p)" width="53" height="53" alt=""> </div> </div> </div> </template> <script> import s1_0 from "@assets/pic1_sel.png"; import s1_1 from "@assets/pic1_normal.png"; import s2_0 from "@assets/pic2_sel.png"; import s2_1 from "@assets/pic2_normal.png"; import s3_0 from "@assets/pic3_sel.png"; import s3_1 from "@assets/pic3_normal.png"; export default { props: { 'options': { type: Object, default: [1, 2, 3, 4] } }, data() { return { selected: null } }, methods: { isSelected(val) { return val === this.selected; }, imgUrl(val) { let isSel = this.isSelected(val); switch(val) { case 1: case 2: return (isSel ? s1_0 : s1_1); case 3: case 4: return (isSel ? s2_0 : s2_1); default: return (isSel ? s3_0 : s3_1); } } } } </script> </code>
<template>
    <div class="hstack gap-3 mx-auto">
        <div class="form-check border" v-for="p in options" :key="p">
            <div class="vstack gap-1">
                <input class="form-check-input" type="radio" name="example" v-model="selected">
                <img :src="imgUrl(p)" width="53" height="53" alt="">
            </div>
        </div>
    </div>
</template>

<script>

import s1_0 from "@assets/pic1_sel.png";
import s1_1 from "@assets/pic1_normal.png";

import s2_0 from "@assets/pic2_sel.png";
import s2_1 from "@assets/pic2_normal.png";

import s3_0 from "@assets/pic3_sel.png";
import s3_1 from "@assets/pic3_normal.png";

export default {
    props: {
        'options': {
            type: Object,
            default: [1, 2, 3, 4]
        }
    },
    data() {
        return {
            selected: null
        }
    },
    methods: {
        isSelected(val) {
            return val === this.selected;
        },
        imgUrl(val) {
            let isSel = this.isSelected(val);
            switch(val) {
                case 1:
                case 2:
                    return (isSel ? s1_0 : s1_1);

                case 3:    
                case 4:
                    return (isSel ? s2_0 : s2_1);
                    
                default:
                    return (isSel ? s3_0 : s3_1);
            }
        }
    }
}
</script>

References:

  • Static Asset Handling of Vue3

Memo:

About require solution.

  • “Cannot find require variable” error from browser. So the answer with require not working for me.
  • It seems nodejs >= 14 no longer has require by default. See this thread. I tried the method, but my Vue3 + vite give me errors.

In Nuxt3 I made a composable that is able to be called upon to import dynamic images across my app. I expect you can use this code within a Vue component and get the desired effect.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>const pngFiles = import.meta.glob('~/assets/**/*.png', {
//@ts-ignore
eager: true,
import: 'default',
})
export const usePNG = (path: string): string => {
// @ts-expect-error: wrong type info
return pngFiles['/assets/' + path + '.png']
}
</code>
<code>const pngFiles = import.meta.glob('~/assets/**/*.png', { //@ts-ignore eager: true, import: 'default', }) export const usePNG = (path: string): string => { // @ts-expect-error: wrong type info return pngFiles['/assets/' + path + '.png'] } </code>
const pngFiles = import.meta.glob('~/assets/**/*.png', {
  //@ts-ignore
  eager: true,
  import: 'default',
})

export const usePNG = (path: string): string => {
  // @ts-expect-error: wrong type info
  return pngFiles['/assets/' + path + '.png']
}

sources

The other examples worked for me. An alternative however is to import it like this:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code><script setup>
...other code
onMounted(() => {
getImageUrl()
})
const url = ref()
const getImageUrl = async () => {
url.value = (await import(/* @vite-ignore */`../assets/images/${dynamicValue.value}.png`)).default
}
</script>
<template>
<img :src="url">
</template>
</code>
<code><script setup> ...other code onMounted(() => { getImageUrl() }) const url = ref() const getImageUrl = async () => { url.value = (await import(/* @vite-ignore */`../assets/images/${dynamicValue.value}.png`)).default } </script> <template> <img :src="url"> </template> </code>
<script setup>
  ...other code

  onMounted(() => {
    getImageUrl()
  })

  const url = ref()
  const getImageUrl = async () => {       
    url.value = (await import(/* @vite-ignore */`../assets/images/${dynamicValue.value}.png`)).default
  }
</script>
<template>
  <img :src="url">
</template>

(vue3/vite4/node18)

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code><template>
<img :src="getImageUrl(imageName)" alt="Avatar" class="size-10" />
</template>
<script setup>
import { ref } from "vue";
const imageName = ref('df0e160d30bdbf8e0706c92ebf148036.png');
function getImageUrl(name) {
return new URL(`../../assets/${name}`, import.meta.url).href;
}
</script>
<style scoped>
.size-10 {
width: 10rem;
height: 10rem;
}
</style>
</code>
<code><template> <img :src="getImageUrl(imageName)" alt="Avatar" class="size-10" /> </template> <script setup> import { ref } from "vue"; const imageName = ref('df0e160d30bdbf8e0706c92ebf148036.png'); function getImageUrl(name) { return new URL(`../../assets/${name}`, import.meta.url).href; } </script> <style scoped> .size-10 { width: 10rem; height: 10rem; } </style> </code>
<template>
  <img :src="getImageUrl(imageName)" alt="Avatar" class="size-10" />
</template>

<script setup>
import { ref } from "vue";

const imageName = ref('df0e160d30bdbf8e0706c92ebf148036.png');

function getImageUrl(name) {
  return new URL(`../../assets/${name}`, import.meta.url).href;
}
</script>

<style scoped>
.size-10 {
  width: 10rem;
  height: 10rem;
}
</style>

1

For any newcomers on google, here is the method I used by piecing other answers in this thread together. My use-case is that I wanted to be able to specify a condition to load an image on a component and if that condition wasn’t met, to use an alternate image. I tried to use :src and :alt separately, but when a 404 was eventually thrown, the code which was supposed to catch the error never did.

Give this a try if you want to load an image if an image is found, and a different image otherwise:

script:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>getImage(mainImage, altImage) {
try {
const modules = import.meta.glob('@/assets/items/*.png', { eager: true, import: 'default' })
const moduleKeys = Object.keys(modules)
const mainImagePath= moduleKeys.find((key) => key.includes(mainImage))
const altImagePath= moduleKeys.find((key) => key.includes(altImage))
return mainImagePath? modules[mainImagePath] : modules[altImagePath]
} catch (err) {
console.error(err)
}
}
...
props: {
mainImage: {
type: String,
default: ""
},
altImage: {
type: String,
default: ""
}
}
</code>
<code>getImage(mainImage, altImage) { try { const modules = import.meta.glob('@/assets/items/*.png', { eager: true, import: 'default' }) const moduleKeys = Object.keys(modules) const mainImagePath= moduleKeys.find((key) => key.includes(mainImage)) const altImagePath= moduleKeys.find((key) => key.includes(altImage)) return mainImagePath? modules[mainImagePath] : modules[altImagePath] } catch (err) { console.error(err) } } ... props: { mainImage: { type: String, default: "" }, altImage: { type: String, default: "" } } </code>
getImage(mainImage, altImage) {
        try {
          const modules = import.meta.glob('@/assets/items/*.png', { eager: true, import: 'default' })
          const moduleKeys = Object.keys(modules)
          const mainImagePath= moduleKeys.find((key) => key.includes(mainImage))
          const altImagePath= moduleKeys.find((key) => key.includes(altImage))

          return mainImagePath? modules[mainImagePath] : modules[altImagePath]
        } catch (err) {
          console.error(err)
        }
      }

...
props: {
   mainImage: {
     type: String,
     default: ""
   },
   altImage: {
     type: String,
     default: ""
   }
}

and in the template (I am using Vuetify):

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code><v-card class="pa-2">
<v-row>
<v-col>
<v-avatar color="white">
<v-img
:src="getImage(mainImage, altImage)"
>
</v-img>
</v-avatar>
</v-col>
<v-btn size="x-small" icon>
<v-icon>mdi-plus</v-icon>
</v-btn>
</v-col>
</v-row>
</v-card>
</code>
<code><v-card class="pa-2"> <v-row> <v-col> <v-avatar color="white"> <v-img :src="getImage(mainImage, altImage)" > </v-img> </v-avatar> </v-col> <v-btn size="x-small" icon> <v-icon>mdi-plus</v-icon> </v-btn> </v-col> </v-row> </v-card> </code>
<v-card class="pa-2">
    <v-row>
      <v-col>
        <v-avatar color="white">
          <v-img
            :src="getImage(mainImage, altImage)"
          >
          </v-img>
        </v-avatar>
      </v-col>
        <v-btn size="x-small" icon>
          <v-icon>mdi-plus</v-icon>
        </v-btn>
      </v-col>
    </v-row>
  </v-card>

Use Vite’s API import.meta.glob works well, I refer to steps from docs of webpack-to-vite. It lists some conversion items and error repair methods. It can even convert an old project to a vite project with one click. It’s great, I recommend it!

  1. create a Model to save the imported modules, use async methods to dynamically import the modules and update them to the Model
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>// src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
const assets = import.meta.glob('../assets/**')
Vue.use(Vuex)
export default new Vuex.Store({
state: {
assets: {}
},
mutations: {
setAssets(state, data) {
state.assets = Object.assign({}, state.assets, data)
}
},
actions: {
async getAssets({ commit }, url) {
const getAsset = assets[url]
if (!getAsset) {
commit('setAssets', { [url]: ''})
} else {
const asset = await getAsset()
commit('setAssets', { [url]: asset.default })
}
}
}
})
</code>
<code>// src/store/index.js import Vue from 'vue' import Vuex from 'vuex' const assets = import.meta.glob('../assets/**') Vue.use(Vuex) export default new Vuex.Store({ state: { assets: {} }, mutations: { setAssets(state, data) { state.assets = Object.assign({}, state.assets, data) } }, actions: { async getAssets({ commit }, url) { const getAsset = assets[url] if (!getAsset) { commit('setAssets', { [url]: ''}) } else { const asset = await getAsset() commit('setAssets', { [url]: asset.default }) } } } }) </code>
// src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
const assets = import.meta.glob('../assets/**')
Vue.use(Vuex)
export default new Vuex.Store({
  state: {
    assets: {}
  },
  mutations: {
    setAssets(state, data) {
      state.assets = Object.assign({}, state.assets, data)
    }
  },
  actions: {
    async getAssets({ commit }, url) {
      const getAsset = assets[url]
      if (!getAsset) {
        commit('setAssets', { [url]: ''})
      } else {
        const asset = await getAsset()
        commit('setAssets', { [url]: asset.default })
      }
    }
  }
})
  1. use in .vue SFC
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>// img1.vue
<template>
<img :src="$store.state.assets['../assets/images/' + options.src]" />
</template>
<script>
export default {
name: "img1",
props: {
options: Object
},
watch: {
'options.src': {
handler (val) {
this.$store.dispatch('getAssets', `../assets/images/${val}`)
},
immediate: true,
deep: true
}
}
}
</script>
</code>
<code>// img1.vue <template> <img :src="$store.state.assets['../assets/images/' + options.src]" /> </template> <script> export default { name: "img1", props: { options: Object }, watch: { 'options.src': { handler (val) { this.$store.dispatch('getAssets', `../assets/images/${val}`) }, immediate: true, deep: true } } } </script> </code>
// img1.vue
<template>
  <img :src="$store.state.assets['../assets/images/' + options.src]" />
</template>
<script>
export default {
  name: "img1",
  props: {
    options: Object
  },
  watch: {
    'options.src': {
      handler (val) {
        this.$store.dispatch('getAssets', `../assets/images/${val}`)
      },
      immediate: true,
      deep: true
    }
  }
}
</script>

This worked for me

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code><img v-for="image in project.images" :key="image.index" :src="`${getImagePath(image.src)}`" :alt="image.alt" />
getImagePath(image) {
const imgUrl = `/nestedPublicFolder/${image}.jpg`;
return imgUrl;
</code>
<code><img v-for="image in project.images" :key="image.index" :src="`${getImagePath(image.src)}`" :alt="image.alt" /> getImagePath(image) { const imgUrl = `/nestedPublicFolder/${image}.jpg`; return imgUrl; </code>
<img v-for="image in project.images" :key="image.index" :src="`${getImagePath(image.src)}`" :alt="image.alt" />
getImagePath(image) {
   const imgUrl = `/nestedPublicFolder/${image}.jpg`;
   return imgUrl;

},

If you want to support ‘@/’, you can use next function:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>export const loadFile = (file: string) => {
let path = file;
let baseUrl = '';
if (file.startsWith('@/')) {
path = file.replace('@/', '');
}
return new URL(`./${path}`, import.meta.url).href;
}
</code>
<code>export const loadFile = (file: string) => { let path = file; let baseUrl = ''; if (file.startsWith('@/')) { path = file.replace('@/', ''); } return new URL(`./${path}`, import.meta.url).href; } </code>
export const loadFile = (file: string) => {
  let path = file;
  let baseUrl = '';
  if (file.startsWith('@/')) {
    path = file.replace('@/', '');
  } 
  return new URL(`./${path}`, import.meta.url).href;
}

Working for me for "vite": "^5.2.13" in latest Vue CLI setup

Why: ‘@’ is not getting resolved in production build but ./ working fine

If you have a limited number of images to use, you could import all of them like this into your component. You could then switch them based on a prop to the component.

3

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật