Webpack: load images from inline style (background-image)

Viewed 4764

I have a problem with loading an image using webpack.

When I use an image tag on an html page, everything works fine:

 <img src="../../content/images/logo.png">

But I also have a need to use inline styles:

<div style="background-image: url('../../content/images/logo.png')">

In this case, webpack doesn't resolve the images and leaves the string in the url() untouched.

Webpack config:

        module: {
        rules: [
            { test: /bootstrap\/dist\/js\/umd\//, loader: 'imports-loader?jQuery=jquery' },
            {
                test: /\.ts$/,
                loaders: [
                    'angular2-template-loader',
                    'awesome-typescript-loader'
                ],
                exclude: ['node_modules/generator-jhipster']
            },
            {
                test: /\.html$/,
                loader: 'html-loader',
                options: {
                    minimize: true,
                    caseSensitive: true,
                    removeAttributeQuotes:false,
                    minifyJS:false,
                    minifyCSS:false
                },
                exclude: ['./src/main/webapp/index.html']
            },
            {
                test: /\.scss$/,
                loaders: ['to-string-loader', 'css-loader', 'sass-loader'],
                exclude: /(vendor\.scss|global\.scss)/
            },
            {
                test: /(vendor\.scss|global\.scss)/,
                loaders: ['style-loader', 'css-loader', 'postcss-loader', 'sass-loader']
            },
            {
                test: /\.css$/,
                loaders: ['to-string-loader', 'css-loader'],
                exclude: /(vendor\.css|global\.css)/
            },
            {
                test: /(vendor\.css|global\.css)/,
                loaders: ['style-loader', 'css-loader']
            },
            {
                test: /\.(jpe?g|png|gif|svg|woff2?|ttf|eot)$/i,
                loaders: ['file-loader?hash=sha512&digest=hex&name=/images/[hash].[ext]']
            }
        ]
    },

I would be grateful if someone could help me with this issue.

7 Answers

Googled myself for solution, and decided to make a loader. https://github.com/apotap2/url-replace-loader It's just 20 lines of code :D it just that replaces every 'url(.*)' with require() so you can use it with file-loader. If you need more complicated logic it seems it's faster to make a loader yourself.

Other solution would be (if you dont want to use interpolate flag)

$scope.imageToUse =  require(`../../content/images/logo.png`)

in your controller

and use directly in your background: url({{imageToUse }})

Internally webpack will encode image to base64 and that will be assigned to imageToUse variable which than will rendered to your html page.

This solution is related to implementing webpack in Angular 1.X project. Similar approach can be applied in other cases.

On webpack 5, the correct answer will be to use your html-loader.

Change your inline styles:

 ## Heading ##<div background-image="../../content/images/logo.png">

and your html-loader:

  {
    test: /\.html$/i,
    loader: 'html-loader',
    options: {
      minimize: true,
      attributes: {
        list: [
          '...',// All default supported tags and attributes
          {
            tag: 'div',
            attribute: 'data-background',
            type: 'src',
          }
        ]
      }
    }
  }

Here for more details.

The most straightforward solution for me was to use interpolate option in Webpack as mentioned in this answer.

{
  test: /\.(html)$/,
  include: path.join(__dirname, 'src/views'),
  use: {
    loader: 'html-loader',
    options: {
      interpolate: true
    }
  }
}

And then in your HTML

 <div style="background-image: url('${require('../../content/images/logo.png')')">

Very basic just:

<div class="background-design-element" style="background-image: url('<%= require('./../assets/images/test.jpg') %>')"></div>

Key is to remember: <%= =%> It can actually be this simple - :-)

interpolate option is removed Since 1.0.0

check the change log here : https://github.com/webpack-contrib/html-loader/blob/master/CHANGELOG.md

Since 1.0.0 , interpolate option is removed, so using url('{require(...)}') is no longer possible.

The alternative I am taking now is to use CopyWebpackPlugin to clone all background-image source into dist (the ouput) folder, and use relative path to get source file in html.

<div style="background-image:url('../assets/images/xx.png')">

But using this way you will also lose the tree-shaking of those background-images source, and it's actually not possible to use source alias in url(), so it is just a temporary way :(

const CopyPlugin = require('copy-webpack-plugin');
...

plugins: [
   ...,
   new CopyPlugin(
      {
        patterns: [
          ...
          { 
            from: 'src/assets/images/background-image',
            to: 'assets/images/background-image',
          }
          
        ]
      }
    )

]

Related