How to require() a image only if it exist or else require() the default?

Hi, I 'm new to vue.js, nuxt.js, webpack tooling. My project is a nuxt-create-app.

First I have to say it is amazing toolset that I was looking for a long time. So exiting to do new projects with it.

My problem is I have an image that I want to check if it exist first and if not, require the default image.

I made a function in Vue filters with a JS try catch, but a got the error: Cannot find module '@/assets/img/beer-default.png'.

I try different approach to reference the path like ./assets… or .assets… but It didn’t work neither.

I think node require() is working because of the error message I receive. The path reference is probably wrong and maybe Webpack can not resolve the path (@ or .) in the function.

I also want that you know that :src="require('@/assets/img/beer-' + productId + '.png')" is working.

If someone can help me to find the right way to refers the relative path in a Vue filters function or if I’m doing something wrong or impossible, I would really appreciate.

Here’s the code

<img
    :src="'@/assets/img/beer-' + productId + '.png' | loadImgOrPlaceholder('@/assets/img/beer-default.png')"
    :alt="$t('brand_name') + ' ' + product.name"
    class="product-img is-block"
>
<script>
export default {
  filters: {
    loadImgOrPlaceholder: function (path, fallback) {
      let img = ""
      try {
        img = require(path)
        return img
      } catch (e) {
        img = require(fallback)
        return img
      }
    }
  }
}
</script>

Thank you for your attention

Have you tried to use tilde?

<img src="~@/assets/image.jpg"/>

A tilde is used to let webpack know that you are trying to refer the src link to the relative path from webpack source.

I am no expert, but I would use a method since it is way easier to read.

<img
    :src="'getBeerImage(productId)
    :alt="$t('brand_name') + ' ' + product.name"
    class="product-img is-block"
>

and simple method based on your filter:

getBeerImage(img) {
    let defaultImage = "@/assets/img/beer-default.png" // just set default
    let path = `@/assets/img/beer-${img}.png`
      try {
        return require(path)
      } catch (e) {
        return defaultImage
      }
}

Something like that (untested)

Side note, I think in theory you should know if there is an image for the product and save all the if else from the client side altogether. Server side when you fetch the data if database has for example products.image field is null then return the default image or the saved field in the database. So the front end gets an image from your products array / object no matter what without forcing the client side to figure it out.

1 Like

@chenxeed I tried it but got the same error.
@movepixels Filters is what seems to be the appropriate way to filter the loaded image. And as I generate the website to be static, the data aren’t fetch. Also images can be other type so I can’t write the default image in the function.

Thank you for trying to help me.

You can set an onerror handler function with @error :

<img :src="require('@/assets/someImg.jpg')" @error="imgError">

If the image fails to load, replace it with the default image.

data(){
  return{
    defaultImg: 'require('@/assets/default.png')'
  }
},
methods:{
 imgError(){
  this.src = this.defaultImg;
 } 
}

Afaik, you cannot require a dynamic value. It must be a static value so webpack can generate the targeted assets.

@regchiu thank you but I saw this answer in Stack Overflow, and it doesn’t work.

@chenxeed You mean my “required” images will not be generated? If it is true, how I can refers an image with a dynamic value like this: '@/assets/img/beer-' + productId + '.png')"

Thank you guys for taking your time and trying to help me with this problem.

@chenxeed is correct, Webpack must know the image string to use when compiling, it can’t be conditional.

@regchiu’s method is possible if both images can be resolved, however the values need to be assigned within data to be reactive. The problem is the build will fail if Webpack can’t find the image, so the solution, as it stands, is void.

What you can do instead is skip processing the image(s) with Webpack and store them as static assets. You can then check for them on the FE and use a fallback if the image returns an error. https://jsfiddle.net/jamesbrndwgn/7p8wdqz0/

1 Like

It was hard but I found a way

try this:

let imgName = "logo-blue.svg";

getLogo(imgName) {
const partName = imgName.split("logo-")[1];
return require("../img/logo-" + partName);
}

I don’t know why, but if you do return require("../img/" + imgName); it doesn’t work, just with the split it’s working.