laravel-mix: Vue asset images not loading

  • Laravel Mix Version: 6.0.9
  • Node Version: 15.5.1
  • NPM Version: 7.3.0
  • OS: Mac OS Big Sur 11.1

Description:

After upgrading to Laravel Mix 6 my build runs successfully but images referenced from the assets folder no longer are loading.

Steps To Reproduce:

Running an existing Laravel v8.21.0 project with Laravel/UI Vue auth scaffolding, after upgrading Laravel Mix to version 6 images referenced from the assets folder in Vue don’t err out on build but display as broken images in the browser.

Referencing images as such:

<img src="../assets/logo.png" alt="Logo">

When I inspect the image I see this:

<img src="[object Module]" alt="Logo">

Any help would be greatly appreciated. Thanks!

About this issue

  • Original URL
  • State: open
  • Created 3 years ago
  • Reactions: 10
  • Comments: 16

Most upvoted comments

Apparently its a bug with vue-loader which Mix uses. Here is my workaround solution. This only sets esModule to false on the images file-loader.

mix.override(webpackConfig => {
  // BUG: vue-loader doesn't handle file-loader's default esModule:true setting properly causing
  // <img src="[object module]" /> to be output from vue templates.
  // WORKAROUND: Override mixs and turn off esModule support on images.
  // FIX: When vue-loader fixes their bug AND laravel-mix updates to the fixed version
  // this can be removed
  webpackConfig.module.rules.forEach(rule => {
    if (rule.test.toString() === '/(\\.(png|jpe?g|gif|webp)$|^((?!font).)*\\.svg$)/') {
      if (Array.isArray(rule.use)) {
        rule.use.forEach(ruleUse => {
          if (ruleUse.loader === 'file-loader') {
            ruleUse.options.esModule = false;
          }
        });
      }
    }
  });
});

@Aiwings Thanks for the response.

I was also able to get the image loaded by doing this:

<img :src="require('./assets/logo.png').default" alt="Logo">

Both of these solutions feel kinda ugly to me tbh.

@fylzero Yes, the same happened for me (although I never had it working before). The reason seems to be that Mix added support for AVIF images in the loader, altering the regular expression used. Updating the override accordingly made the fix work again:

mix.webpackConfig(webpack => {
    return {
        module: {
            rules: [{
-                test: /(\.(png|jpe?g|gif|webp)$|^((?!font).)*\.svg$)/,
+                test: /(\.(png|jpe?g|gif|webp|avif)$|^((?!font).)*\.svg$)/,
                use: [{
                    loader: require.resolve('file-loader'),
                    options: {
                        esModule: false
                    }
                }]
            }]
        }
    };
});

Overriding the file-loader via mix.webpackConfig() worked for me:

mix.webpackConfig(webpack => {
    return {
        module: {
            rules: [{
                test: /(\.(png|jpe?g|gif|webp)$|^((?!font).)*\.svg$)/,
                use: [{
                    loader: require.resolve('file-loader'),
                    options: {
                        esModule: false
                    }
                }]
            }]
        }
    };
});

The test regex has to match the default Webpack config in order to override the rule. I used mix.dump() to find out the default Webpack config.

The config set using mix.override() seemed to be ignored in Mix 6.

@loilo and mix also updated the loader from loader: 'file-loader' using path project/node_modules/file-loader/dist/cjs.js' for me instead of comparing regex strings or writing your own rules i used this solution

const mix = require('laravel-mix')

mix.extend('disabledEsModule', (config) => {
  config.module.rules.forEach((rule) => {
    let regex = new RegExp(rule.test)

    // why .jpg? because it's one of those file extensions that is someone never left him behind.
    if (regex.test('.jpg')) {
      rule.use.forEach((use) => {
        if (use.loader.includes('file-loader')) {
          Object.assign(use.options, { esModule: false })
        }
      })
    }
  })
})

or use this package for more simple https://www.npmjs.com/package/laravel-mix-fileloader

@tinyfly I finally got around to implementing your solution and it works great! Thank you for that and for linking to the issue in vue-loader.

Apparently its a bug with vue-loader which Mix uses. Here is my workaround solution. This only sets esModule to false on the images file-loader.

mix.override(webpackConfig => {
  // BUG: vue-loader doesn't handle file-loader's default esModule:true setting properly causing
  // <img src="[object module]" /> to be output from vue templates.
  // WORKAROUND: Override mixs and turn off esModule support on images.
  // FIX: When vue-loader fixes their bug AND laravel-mix updates to the fixed version
  // this can be removed
  webpackConfig.module.rules.forEach(rule => {
    if (rule.test.toString() === '/(\\.(png|jpe?g|gif|webp)$|^((?!font).)*\\.svg$)/') {
      if (Array.isArray(rule.use)) {
        rule.use.forEach(ruleUse => {
          if (ruleUse.loader === 'file-loader') {
            ruleUse.options.esModule = false;
          }
        });
      }
    }
  });
});

I had this issue when i’ve updated laravel mix from 5 to 6

Got some [object Module] urls for thirds libraries like trumbowyg or remixicons and this solved the issue

Thanks, i hope there will be a fix for this or an option to set the esModule option for file-loader !