30

I switched from the Vue CLI to Vite CLI, and from the Composition API of Vue 3 to SFC Script setup API.

How it previously worked for me

When I was using the official Vue CLI, I could import an image source by passing the filename of the path by the props :

<template>
  <img :src="require(`@/assets/${imagePath}`)"/>
<template/>

<script>
export default {
  props: {
    imagePath: { type: String },
  },
  setup() {
    // ...
  }
}
<script/>

And then call it like this :

<template>
  <Image imagePath="icon.png" />
</template>

The error I get since I migrated to Vite

But since I migrated to the Vite CLI, I have an error "Uncaught ReferenceError: require is not defined". My file now use the script setup syntax and looks like this :

<script setup>
const props = defineProps({
  imagePath: { type: String },
})
</script>

<template>
  <img :src="require(`@/assets/${props.imagePath}`)"/>
</template>

require is not define error

What I tried

I already tried to import the file directly from the assets folder with a relative path, and it worked. But I cannot specify the path from props with the import statement.

<script setup>
// Works but do not use the props, so the component is not reusable
import logo from "./../assets/logo.png"
</script>

<template>
  <img :src="logo"/>
</template>
<script setup>
// Component is reusable but the import statement has illegal argument I guess
const props = defineProps({
  imagePath: { type: String },
})

import logo from `./../assets/${props.imagePath}`
</script>

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

I also tried the import statement in the template but it cannot even compile the code :

<script setup>
const props = defineProps({
  imagePath: { type: String },
})
</script>

<template>
  <img :src="import `./../assets/${props.iconPath}`" />
</template>

Am I missing something ? Maybe a plugin exists and can help me achieve this ?

Dony
  • 1,543
  • 2
  • 10
  • 25

5 Answers5

38

I also encountered this problem. I searched about this and found according to this github issue comment,

There should never be require in source code when using Vite. It's ESM only.

More about this on Features | Vite - Static Assets

A bit of searching lead me to this Vue 3 code sample link that worked for me

<CarouselItem v-for="(item,index) of carouselData" :key="index">
 <img :src="getImageUrl(item.img_name)" />
</CarouselItem>
setup() {
  const getImageUrl = (name) => {
        return new URL(`../../lib/Carousel/assets/${name}`, import.meta.url).href
    }
  return { carouselData, getImageUrl }
}
leipzy
  • 11,676
  • 6
  • 19
  • 24
  • 1
    Is this working for you when running a production build? – blabbath Mar 24 '22 at 09:10
  • 1
    My bad - just in case someone stumbles upon this: I had a JS file in my assets folder that was causing trobles. Now with only .jpg and .svg files it works like a charm! – blabbath Mar 24 '22 at 09:13
8

In case you're using require.context, the new way seems to be glob import. Change your old statement from:

const locales = require.context("../../lang", true, /[A-Za-z0-9-_,\s]+\.json$/i)

to:

const locales = import.meta.glob('../../lang/*.json')

Edit:

This also seems to replace require.

Artur Müller Romanov
  • 4,417
  • 10
  • 73
  • 132
1

Use a watcher on the imagePath prop that dynamically imports the image, and updates a ref (bound to the <img>.src) with the result:

<script setup>
import { ref, watchEffect } from 'vue'

const props = defineProps({
  imagePath: { type: String },
})

const logo = ref()
watchEffect(async () => {
  logo.value = (await import(/* @vite-ignore */ `../assets/${props.imagePath}`)).default
})
</script>

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

demo

tony19
  • 125,647
  • 18
  • 229
  • 307
0

for those who will use static assets with vue + vite, they can use this method.

import imgUrl from '../../img/bgimg.jpg'

export default {
     data() {
    return {
      bgImage: imgUrl
    };
  },

};

then you can use it like this

<div class="ms-auto h-100 z-index-0 ms-n6"
    :style="{
    backgroundImage:
    'url(' +
    bgImage +
    ')',
    }"
></div>
0

If none of provided answers is not help (as in my case) - try to use <slot/> instead, as shown below:

//card component (child)
<template>
  <div class="card">
    <slot />
  </div>
</template>

//services component (parent)
<template>
  <ServiceCard>
    <img src="@/assets/icons/service-1.svg" alt="svg image" />
  </ServiceCard>

  <ServiceCard>
    <img src="@/assets/icons/service-2.svg" alt="svg image" />
  </ServiceCard>
</template>
  • 1
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jul 12 '23 at 08:36