Unable to resolve absolute url() paths for background images in CSS with Webpack
Solution 1
One possible solution that I could come up with was the following:
Use resolve-url-loader
(immediately after sass-loader
):
style!${CSS_LOADER}!autoprefixer!resolve-url!${SASS_LOADER}
Then, define a resolve.alias for static images:
resolve: {
alias: {
images: path.join(__dirname, 'public/images')
}
}
And then in the CSS, you can point to the images like so:
:local .SomeClass {
background: url('images/bg.png');
}
Depending on your URL structure, you may also need to tweak the url-loader
name param:
{
test: /\.(jpe?g|png|gif|svg)$/,
loader: 'url-loader?limit=10000&name=images/[name].[ext]'
}
So I don't know if there's a cleaner way to solve this issue, but that's the best I could come up with so far. I'd welcome any feedback or alternatives.
Thanks!
UPDATE 6/30/2016
There's a couple PR's out that would address this issue, but the maintainer prefers the solution to be in the CSS AST rather than in the CSS loader...
https://github.com/webpack/style-loader/pull/124 https://github.com/webpack/style-loader/pull/96
Here's hoping a real fix happens soon...
Solution 2
With webpack 2:
In your .scss files use ~ before the path.
.yourClass {
background: url('~img/wallpaper.png');
}
Make use of the resolve root from webpack, add this to your webpack.config.js:
resolve: {
modules: [
path.resolve(root),
'node_modules'
]
},
It should work also for the @import, eg @import "~otherfile.scss"
Muers
Updated on June 04, 2022Comments
-
Muers almost 2 years
I have the following Webpack config (roughly, it has been simplified for this post):
const rootPublic = path.resolve('./public'); const LOCAL_IDENT_NAME = 'localIdentName=[name]_[local]_[hash:base64:5]'; const CSS_LOADER = `css?sourceMap&${LOCAL_IDENT_NAME}&root=${rootPublic}`; const SASS_LOADER = 'sass?sourceMap&includePaths[]=' + path.resolve(__dirname, './src/styles'); // ... loaders: loaders: [ { test: /\.(jpe?g|png|gif|svg)$/, loader: 'url-loader?limit=10000&name=[path][name].[ext]' }, { test: /\.scss$/, loader: config.DEVELOPMENT_MODE ? `style!${CSS_LOADER}!autoprefixer!${SASS_LOADER}` : ExtractTextPlugin.extract('style', `${CSS_LOADER}!autoprefixer!${SASS_LOADER}`) }, // ... ]
Now this works perfectly fine for images referenced normally in scss files:
.some-static-class { background: url('/images/bg.png'); }
However, it does not work when using the
:local
directive::local .SomeClass { background: url('/images/bg.png'); }
And I think that's because
root
is defined for the CSS loader. I get a build error:Module not found: Error: Cannot resolve 'file' or 'directory' ./../../../../../../../../images/bg.gif
If instead I remove
root
from the css-loader config, then it builds fine but then the path "looks" correct in Chrome's inspector, but when you actually open the link in a new tab, it points to:chrome-devtools://devtools/bundled/"/images/bg.png"
which obviously doesn't resolve correctly.I'm not sure if the problem is with the
url-loader
config, or what's going on exactly.I played around with different webpack configs to specify the resolve.root, resolve.modulesDirectories, etc but totally not sure if they're having any affect or if I'm just going about it completely wrong. I also came across
resolve-url-loader
but not sure if that's even what I need at all.Any ideas? MTIA!
UPDATE
I should note, that it works fine in Safari, but not in Chrome. So it seems like a Chrome-specific bug, but it's not practical to have to do all our development in Safari.
I also came across, vue-style-loader, which is a fork of style-loader that claims to fix this issue, but the way it fixes it is by relying on a hacky deprecated escape/unescape method.