Vite / Vue 3 : "require is not defined" when using image source as props

Viewed 13814

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 ?

3 Answers

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 }
}

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

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')
Related