How to create a dynamic background image for components in Vue using props?


I want to pass the path of an image as a prop to a component. I want the component to use the prop to dynamically generate the background image.

All my images in an assets folder in the Vue src folder. The path looks like '@/assets/images/subject/computers.jpeg'


No background image appears

This is what renders on the page:

enter image description here

However, nothing shows up

Strange Behaviour

For some reason, adding the exact same path name '@/assets/images/subject/computers.jpeg' in CSS works (Adding it in the <style> tags). It just doesn't work if it's v-bind inline style.

Here is what it looks like in my CSS

enter image description here

The problem with this though, is that it is not dynamically rendered CSS.

I inspected the element further and noticed some strange behavior. As you can see, the inline style is reading the image path as '@/assets/images/subject/computers.jpeg' (element.style in image below).

Whereas adding the path in CSS, changes '@/assets/images/subject/computers.jpeg' to 'http://localhost:8080/img/computers.7f3748eb.jpeg', which then correctly renders the background image.

enter image description here


I suppose my question is twofold:

  1. How would you dynamically render a background image in Vue with props? Or is there a better way to do this?
  2. Why does binding inline styles not resolve the path (keeps using '@'), while adding it directly in the <script> tags does resolve the path?


Okay, big thank you to tao & Bernard Borg for helping me wrap my head around this.

I was able to solve this using computed properties like so:

const imgSrc = computed(() => require(`@/assets/images/subject/${props.subject.image}`))

Because of the way VueLoader (which is default in Vue) resolves the paths, I couldn't store the '@' path in the prop itself. Instead, the prop is just the image/file name (ex. "computers.jpeg"), and the computed property with require() handles resolving the prepended path to the image.

Afterwards, I added the computed property to my binded inline style like so:

<div class="subject-wrapper" v-bind:style="{ backgroundImage: `url('${imgSrc}')` }">

Voila! It finally works.


Pass only the image/file name as a prop, and prepend the path using require() in a computed property.

Cheers 🍻


  • When using Webpack (Vue-cli), the @ symbol is an alias for the src folder.

    If the URL starts with @, it's also interpreted as a module request. This is useful if your webpack config has an alias for @, which by default points to /src in any project created by vue-cli


    If you're using Vite, you probably have the following in vite.config.js;

    resolve: {
         alias: {
              "@": fileURLToPath(new URL("./src", import.meta.url)),

    VueLoader transforms asset urls when they're located in thee following html tags: video, source, img, image (svgs) and use (svgs). Moreover, it also transforms asset urls when they're in the tag if you have VueLoader configured to use css-loader.

    All compiled CSS are processed by css-loader, which parses url() and resolves them as module requests.


    This asset url transformation does not happen when you're v-binding a style property.

    You can use this mechanism to pass transformed paths to a different component (for example, a component from a library). Despite this, you would still need to declare every single path as mentioned by @tao.

    The relevant point here is that VueLoader has to read all the possible paths when compiling the app and resolve them.

    <script setup lang="ts">
    import BackgroundImageViewer from './components/BackgroundImageViewer.vue'
    import { ref, onMounted } from "vue";
    const hiddenImage = ref<HTMLImageElement>();
    const thePath = ref();
    onMounted(() => {
        if (hiddenImage.value) {
            thePath.value = hiddenImage.value.src;
            hiddenImage.value.src = "";
        <img src="@/assets/vue.svg" ref="hiddenImage" hidden />
        <BackgroundImageViewer :path="thePath"/>

    As @tao also mentioned, if the number of different images is too much you can opt to place the images in your project's public folder.

    Edit: You can also use the require method with an if-else tree, ternary or switch statement to get the relative path to your image (if the number of images that can be set as the background isn't too much - otherwise use the public folder).

    With Vite you can use the vite-plugin-require plugin to make require() work.