52

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 ?

8 Answers 8

61

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 }
}
Sign up to request clarification or add additional context in comments.

3 Comments

Is this working for you when running a production build?
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!
The link to static assets with Vite did it for me.
15

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.

2 Comments

this resolved the error but i was using require to dynamically load an image and it fails to load the image
Internal server error: Invalid glob import syntax: Could only use literals Is the error this generates for me
4

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>

Importing a static asset will return the resolved public URL when it is served:

import imgUrl from './img.png'
document.getElementById('hero-img').src = imgUrl

For example, imgUrl will be /img.png during development, and become /assets/img.2d8efhg.png in the production build.

see also: https://vitejs.dev/guide/assets.html#importing-asset-as-url

1 Comment

Nice and clean, thanks!
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

1 Comment

Can you share a link to a reproduction?
1

Just had the same issue. I have a global function that gets the image path depending on product name. That's how I made it work.

Many thanks to @leipzy

export const getProductLogo = (cart) => {

  
  if (cart) {
    let im
    try {
      im = `../assets/img/cart/${cart}.png`
    } catch (err) {
      im = '../assets/img/default-logo.svg'
    }
    return new URL(`${im}`, import.meta.url).href
  }
}

Comments

1

I am currently struggeling with the same thing, with the added complication that the app will be deployed to k8s, where the ingress routing adds another level of absurdity for file paths.

I have tried all suggestions here, nothing worked well for both development and prod.

In the end, I simply kept the pictures I load dynamically in the public folder. Per Vue's own website, this folder is copied but does not go through webpack, so paths remain intact.

It's not super elegant, but it works incredibly well. This finally solved the headache I have been having for weeks over multiple web apps.

Comments

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 Comment

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.
0

Was able to make vite/webpack-agnostic solution with vite-plugin-transform by Silksofthesoul

vite.config.ts:

import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import transformPlugin from 'vite-plugin-transform';

export default defineConfig({
  plugins: [
    vue(),
    transformPlugin({
      callbackArray: [str => str.replace(/__VITE_ONLY_ASSIGN__/gim, '')],
    }),
  ],
  define: {
    'process.env': {},
  },
});

In file with non-working require():

const __VITE_ONLY_ASSIGN__require = (relativePath: string) => {
  return new URL(relativePath, import.meta.url).href;
};

const myImageUrl = require('./my-image.svg');

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.