React & Webpack: Loading and displaying images as background-image - image

I'm creating a banner-like component with image set as a component's background but I can't get it to work. I tried different suggestions posted online to no luck and at the moment I'm not sure whether my mistake is within react code or it's the webpack that doesn't load the files correctly.
Here's my file structure:
AppFolder
- client/
-- components/
-- data/
-- images/
----- banners/
------- frontPageBanner2.png
-------
-- styles/
-- myApp.js
-- store.js
---------
- index.html
- package.json
- webpack.config.dev.js
Here's my webpack configuration:
var path = require('path');
var webpack = require('webpack');
module.exports = {
devtool: 'source-map',
entry: [
'webpack-hot-middleware/client',
'./client/myApp'
],
output: {
path: path.join(__dirname, 'dist'),
filename: 'bundle.js',
publicPath: '/static/'
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin()
],
module: {
loaders: [
// js
{
test: /\.js$/,
exclude: /node_modules/,
loaders: ['babel'],
presets: ['es2015', 'react'],
include: path.join(__dirname, 'client')
},
// CSS
{
test: /\.scss$/,
include: path.join(__dirname, 'client'),
loader: 'style-loader!css-loader!sass-loader'
},
// PNG
{
test: /\.(jpg|png)$/,
loader: "url-loader?limit=25000",
//loader: 'file?name=[path][name].[ext]',
include: path.join(__dirname, 'client')
},
// FontAwesome
{ test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: "url-loader?limit=10000&mimetype=application/font-woff" },
{ test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
exclude: path.join(__dirname, 'client/images'),
loader: "file-loader" },
// other SVG
{ test: /\.svg$/,
loader: 'url-loader',
query: { mimetype: "image/svg+xml" },
include: path.join(__dirname, 'client/images')
},
]
}
};
And here's how I'm using it in the app:
export class Banner extends React.Component {
render() {
console.log(this.props.banners);
// = '../images/banners/frontPageBanner.png'
const bannerImg = this.props.image.backgroundImage;
var bannerStyle = {
background: 'url(' + bannerImg + ')',
backgroundSize: this.props.image.backgroundSize,
backgroundPosition: this.props.image.backgroundPosition,
}
return (
<section key={this.props.key} className="container hero" style={bannerStyle}>
<div className="textbox">
<h2>{this.props.title}</h2>
</div>
</section>
)
}
}
Here's resulting html:
<section class="container hero" style="background: url('../images/banners/frontPageBanner2.png') 100% 100% / contain;">
...
</section>
but no image is displayed. Also opening the image src link shows just a blank screen. Why? And how can i fix it?

You should pass a var that is the result of requiring the image, when you require it using webpack, it'll generate a base64 inline image, not a relative path.
var DuckImage = require('./Duck.jpg');
var bannerStyle = {
backgroundImage: 'url(' + DuckImage + ')',
backgroundSize: this.props.image.backgroundSize,
backgroundPosition: this.props.image.backgroundPosition,
}

Related

How to use preload webpack plugin with blade templates

So i have a laravel/vuejs app , i'm trying to preload the css chunks that i have generated upon splitting components, it works fine but it generates an index.html. Is it possible to somehow do it in blade templates? What could be the solution?
Here is my webpack.config.js
const VuetifyLoaderPlugin = require('vuetify-loader/lib/plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const {VueLoaderPlugin} = require("vue-loader");
const PreloadWebpackPlugin = require('preload-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin')
const path = require("path");
module.exports = {
entry: {
main: "./resources/js/app.js",
},
output: {
publicPath: '/',
path: path.resolve(__dirname, "public"),
},
plugins: [
new HtmlWebpackPlugin(),
new VuetifyLoaderPlugin(),
new VueLoaderPlugin(),
new MiniCssExtractPlugin({
filename: `components/[name].css`
}),
new PreloadWebpackPlugin({
rel: 'preload',
include: 'asyncChunks', // can be 'allChunks' or 'initial' or see more on npm page
fileBlacklist: [/\.map|.js/], // here may be chunks that you don't want to have preloaded
})
],
module: {
rules: [
{
test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
},
"css-loader"
]
},
{
test: /\.js$/,
loader: 'babel-loader',
},
{
test: /\.s[ac]ss$/i,
use: [
// Translates CSS into CommonJS
'css-loader',
// Compiles Sass to CSS
'sass-loader',
],
},
{
test: /\.vue$/,
loader: "vue-loader",
options: {
extractCSS: true
}
},
],
},
resolve: {
extensions: ['.js', '.vue'],
alias: {
'vue$': 'vue/dist/vue.esm.js'
}
},
optimization: {
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/](vue|axios|jquery|bootstrap)[\\/]/,
name: 'vendor',
chunks: 'all'
}
}
}
}
}
And one final question , is it better to preload/prefetch the js chunks too? ( to optimize the performance )

Loading local images with webpack

Hi I am trying to load local images with webpack, it compiles successfully however I get the following error (and no image)
GET http://192.168.1.196:3000/b09d0fa90cacadcad6ce1679aea3d2ee.png 404 (Not Found)
Here is my webpack.config.js file:
const path = require('path')
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'public/scripts'),
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['env']
}
}
},
{ test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192' }
]
},
devServer: {
contentBase: path.resolve(__dirname, 'public'),
publicPath: '/scripts/'
},
devtool: 'source-map'
}
Here is how I am importing the image
import goku from '../public/images/goku.png'
I am trying also with require in the img directly with the same result.
<img src =${require('../public/images/goku.png')}>
<img src =${goku}>
Your problem is actually that you missed a publicPath on output:
output: {
path: path.resolve(__dirname, 'public/scripts'),
filename: 'bundle.js',
publicPath: '/scripts/'
},
devServer: {
contentBase: path.resolve(__dirname, 'public'),
publicPath: '/scripts/'
},
The property publicPath on output has to match the publicPath on devServer.

html loader set wrong path or ignore image from index.html

I have problem in my webpack, when i set attrs: [':data-src'] in my html-loader i have good path in index.html like this: <img src="img/logo.png" width="180" height="96">, but when I use npm run build I haven't images from this index in dist folder. When I have default attrs=img:src i have images, but my path looks like: <img src="../img/../img/logo.png" width="180" height="96">. I tried use attrs: ['data:src', 'img:src'], but it doesn't work.
My config:
var webpack = require('webpack');
var path = require('path');
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
context: path.join(__dirname, "src"),
entry: "./js/index.js",
output: {
path: __dirname + "/dist/js",
filename: "bundle.min.js"
},
module: {
loaders: [{
test: /\.js?$/,
exclude: /(node_modules)/,
loader: 'babel-loader',
query: {
presets: ['es2015'],
}
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.(html)$/,
use: [{
loader: 'html-loader',
options: {
attrs: ['data:src', 'img:src']
}
}]
},
{
test: /\.(jpe?g|png|gif|svg)$/i,
use: [{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: '../img/',
publicPath: '../img/'
}
}]
},
]
},
plugins: [
new webpack.optimize.UglifyJsPlugin({
mangle: false,
sourcemap: false
}),
new HtmlWebpackPlugin({
filename: '../index.html',
template: './index.html'
}),
],
};

How to set up css modules with sass and ExtractTextPlugin at webpack

I am trying to use css modules at webpack but it is not working.
My webpack is:
var path = require('path')
var webpack = require('webpack')
var HappyPack = require('happypack')
var BundleTracker = require('webpack-bundle-tracker')
var path = require('path')
var ExtractTextPlugin = require("extract-text-webpack-plugin")
function _path(p) {
return path.join(__dirname, p);
}
module.exports = {
context: __dirname,
entry: [
'./assets/js/index'
],
output: {
path: path.resolve('./assets/bundles/'),
filename: '[name].js'
},
devtool: 'inline-eval-cheap-source-map',
plugins: [
new BundleTracker({filename: './webpack-stats.json'}),
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
'window.jQuery': 'jquery'
}),
new HappyPack({
id: 'jsx',
threads: 4,
loaders: ["babel-loader"]
}),
new ExtractTextPlugin("[name].css")
],
module: {
loaders: [
{
test: /\.css$/,
include: path.resolve(__dirname, './assets/css/'),
loader: ExtractTextPlugin.extract("style-loader", "css-loader?modules=true&localIdentName=[name]__[local]___[hash:base64:5]!resolve-url-loader")
},
{
test: /\.scss$/,
include: path.resolve(__dirname, './assets/css/'),
loader: ExtractTextPlugin.extract("style-loader", "css-loader?modules=true&localIdentName=[name]__[local]___[hash:base64:5]!resolve-url-loader!sass-loader")
},
{
test: /\.jsx?$/,
include: path.resolve(__dirname, './assets/js/'),
exclude: /node_modules/,
loaders: ["happypack/loader?id=jsx"]
},
{
test: /\.png$/,
loader: 'file-loader',
query: {
name: '/static/img/[name].[ext]'
}
}
]
},
resolve: {
modulesDirectories: ['node_modules'],
extensions: ['', '.js', '.jsx'],
alias: {
'inputmask' : _path('node_modules/jquery-mask-plugin/dist/jquery.mask'),
},
}
}
I am using ExtractTextPlugin and I just add the parameters:
?modules=true&localIdentName=[name]__[local]___[hash:base64:5]
at css and scss loaders.
My React component is:
'use strict'
import React from 'react'
import styles from '../../../css/test.css'
class User extends React.Component{
constructor(props){
super(props)
}
render() {
return (
<nav className={styles.test}>
</nav>
);
}
}
export default User
and my test.css is:
.test {
width:100%;
float: left;
background: blue;
height: 50px;
position: relative;
}
But it renders <nav></nav> with no class.
Can anyone help me?

Is it possible to load SASS in Webpack without bundling fonts BUT with source maps?

I don't want to bundle fonts and images but i need sourceMaps
I have this config (irrelevant parts ommited):
output: {
path: './build/',
publicPath: 'http://localhost:3000/',
filename: '[name].js'
},
module: {
loaders: [
{ test: /\.scss$/, loaders: ['style','css?-url,sourceMap', 'sass?sourceMap'] }
]
}
With this config I get multiple errors in Chrome:
Failed to decode downloaded font: http://localhost:3000/
(index):1 OTS parsing error: invalid version tag
I read a lot of answers and some solutions for similar problem:
1. get rid off 'sourceMap' - it works, and fonts are correctly displayed BUT ... no sourceMaps
2. change publicPath to URL - done it
I cannot find any solution that allows me load fonts outside bundle AND have CSS with sourceMaps...
I just figured it out. The key to solution is ExtractTextPlugin that makes normal link tags instead of 'blobs' and all fonts work super, css are external (even better when they are large), source maps work. Here is my full config if anyone interested:
var path = require('path');
var webpack = require('webpack')
var ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
entry: {
demo: ['./demo/index.ts', 'webpack-dev-server/client?http://localhost:3000'],
lib: ['./src/js/index.js']
},
output: {
path: './build/',
publicPath: '/',
filename: '[name].js'
},
debug: true,
devtool: 'source-map',
resolve: {
extensions: ['', '.ts', '.js']
},
module: {
loaders: [
{ test: /\.tsx?$/, loader: 'ts' },
{ test: /\.coffee$/, loader: 'coffee' },
{ test: /\.(png|jpg)$/, loader: 'url' },
{ test: /\.jsx?$/, loader: 'babel', query: { presets: ['es2015'] }, exclude: /node_modules/, },
{ test: /\.scss$/, loader: ExtractTextPlugin.extract('style', 'css?sourceMap!sass?sourceMap') },
{ test: /\.css$/, loader: ExtractTextPlugin.extract('style', 'css?sourceMap') },
{ test: /\.(ttf|eot|svg|woff(2)?)(\?[\s\S]+)?$/, loader: 'url' }
]
},
devServer: {
contentBase: './demo'
},
plugins: [
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
'window.jQuery': 'jquery'
}),
new ExtractTextPlugin("[name].css", { allChunks: true })
]
};

Resources