I'm getting a flash of unstyled content whenever I reload a page in the most current version of Webpacker 4.x.
I'm mostly using the vanilla config; however, as far as I can tell this takes into account hmr being enabled and switches to style-loader from mini-css-extract-plugin. It's worth mentioning that this is an upgrade from Webpacker v3.5 to 4; I did run rails webpacker:install after the upgrade.
Here's my config:
# Gemfile.lock
webpacker (4.0.0.pre.pre.2)
...
# package.json
"dependencies": {
"@rails/webpacker": "4.0.0-pre.2",
"autoprefixer": "^8.5.1",
"babel-loader": "^7.1.4",
"bootstrap-sass": "^3.3.7",
"case-sensitive-paths-webpack-plugin": "^2.1.2",
"clean-webpack-plugin": "^0.1.19",
"compression-webpack-plugin": "^1.1.11",
"css-hot-loader": "^1.3.9",
"css-loader": "^0.28.11",
"file-loader": "^1.1.11",
"font-awesome": "^4.7.0",
"jquery": "^3.3.1",
"jquery-ujs": "^1.2.2",
"mini-css-extract-plugin": "^0.4.0",
"moment": "^2.22.1",
"node": "^10.2.0",
"node-sass": "^4.9.0",
"popper.js": "^1.14.3",
"postcss-loader": "^2.1.5",
"precss": "^3.1.2",
"resolve-url-loader": "^2.3.0",
"sass-loader": "^7.0.1",
"style-loader": "^0.21.0",
"uglifyjs-webpack-plugin": "^1.2.5",
"url-loader": "^1.0.1",
"webpack-manifest-plugin": "^2.0.3",
"webpack-merge": "^4.1.2"
"devDependencies": {
"webpack-cli": "^2.1.4",
"webpack-dev-server": "^3.1.4"
...
# webpacker.yml
default: &default
source_path: app/javascript
source_entry_path: packs
public_output_path: packs
cache_path: tmp/cache/webpacker
# Additional paths webpack should lookup modules
# ['app/assets', 'engine/foo/app/assets']
resolved_paths: ['vendor/assets/javascripts', 'vendor/assets/stylesheets', 'public/javascripts', 'public/assets/images']
# Reload manifest.json on all requests so we reload latest compiled packs
cache_manifest: false
extensions:
- .js
- .jsx
- .sass
- .scss
- .css
- .module.sass
- .module.scss
- .module.css
- .png
- .svg
- .gif
- .jpeg
- .jpg
development:
<<: *default
compile: true
# Reference: https://webpack.js.org/configuration/dev-server/
dev_server:
https: false
host: localhost
port: 3035
public: localhost:3035
hmr: true
# Inline should be set to true if using HMR
inline: true
overlay: true
compress: true
disable_host_check: true
use_local_ip: false
quiet: false
headers:
'Access-Control-Allow-Origin': '*'
watch_options:
ignored: /node_modules/
test: &test
<<: *default
# Compile test packs to a separate directory
public_output_path: packs-test
production: &production
<<: *default
# Production depends on precompilation of packs prior to booting for performance.
compile: false
# Cache manifest.json for performance
cache_manifest: true
staging:
<<: *production
qa:
<<: *production
integration:
<<: *production
...
# environment.js
const { environment } = require('@rails/webpacker')
const { env } = require('process')
const { resolve } = require('path');
const rootPath = resolve(__dirname, '../..');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const fileLoader = environment.loaders.get('file');
fileLoader.use = [
{
loader: 'url-loader',
options: {
name: '[path][name]-[hash].[ext]',
limit: 10000,
context: 'app/assets',
}
}
]
fileLoader.test = /\.(jpg|jpeg|png|gif|tiff|ico|svg)$/i
environment.loaders.append('font', {
test: /\.(eot|otf|ttf|woff|woff2)$/i,
use: [
{
loader: 'url-loader',
options: {
name: '[path][name]-[hash].[ext]',
limit: 10000,
context: 'node_modules',
}
}
]
})
const pathsToClean = [
'packs',
'packs-test'
]
const cleanOptions = {
root: resolve(rootPath, 'public'),
verbose: true,
}
if (env.NODE_ENV !== 'test') environment.plugins.insert('CleanWebpackPlugin', new CleanWebpackPlugin(pathsToClean, cleanOptions));
module.exports = environment
development.js is vanilla. I'm aware I've a lot of dependencies I can remove from package.json (mostly from playing around with an earlier version of Webpacker) but I assume they're not at fault here.
Apologies if this isn't a ton of info to go on, and am happy to provide anything required. Kudos =)
Edit a day or so later:
I'm going to add a bit to this. When I set hmr and inline to false and have webpack-dev-server running, I had expected to begin seeing the CSS without the flash of unstyled content at the beginning. Instead, I'm _never_ seeing my compiled css and the page remains unstyled.
I'd guess this is somehow due to a misconfiguration on my part with Webpacker; however, I'd have nonetheless expected the default configuration of Webpacker to be close enough to optimal to not require much tinkering on my part, so I'm mentioning this here. I am, of course, also curious what I'm doing wrong 馃槃
I tried a ton of configurations to get hmr working, and nothing works.
I am on 4.0.0.pre.pre.2, and I tried adding each of mini-css-extract-plugin and extract-css-chunks-webpack-plugin
extract-text-webpack-plugin no longer works with Webpack 4.
I've got the same thing, using typescript (awesome-typescript-loader), and scss file, imported into my js.
I'm also seeing the FOUC with webpacker 4.0.2 and Rails 5.2, when using the webpack-dev-server in the Rails development environment
You probably want to set extract_css: true in development that way CSS would be extracted out of JS into a separate bundle and you won't see flashing due to CSS in JS.
webpack-dev-server uses the style-loader which injects the styles from the JS file. To give you an idea of the process:
*something changes* (If you only changed a js file, start at 5.)
<head> typescript-loader like @G-Rath <head>This issue has also been discussed a lot by non-webpackER folks, eg:
mini-css-extract-plugin works for me, but you need to get it just right with weird things like this since mini-css-extract-plugin does not work with WPD:
if (window.isHot()) {
import('./styles.scss');
}
Once you start to understand the steps, you can connect the dots in a way that makes sense for your stack. Hope this helps.
@gauravtiwari setting extract_css = true in development requires that you use <%= stylesheet_pack_tag %> in order to load css.
In general, our team is experiencing this pain point: https://github.com/rails/webpacker/issues/1720#issuecomment-449379412.
Which requires you add the right style sheets with stylesheet_pack_tag whenever a react component uses a stylesheet. Is there a better way to do this?

@derrickmar To solve this problem, we set the style extractor to only extract styles from files following a naming convention, eg: name.index.scss. This allows your react components to import their local styles via JS, thus, you would not need to include those tags.
Here is the regex for the MiniCssExtractPlugin loader: test: /(.+?)\.index\.scss$/
Here is the regex for the local styles loader: test: /^((?!\.index).)+\.s?css$/
This is a batteries not included solution, you will need to somehow override webpackERs internals in order to achieve the behavior you want: https://github.com/rails/webpacker/blob/41d79c96187154b5485289e8c3c42428dd819bfc/package/utils/get_style_rule.js#L41
@jakeNiemiec that's an interesting idea but that doesn't solve FOUC because local styles are still imported via JS as you said.
@derrickmar doesn't solve FOUC because local styles are still imported via JS as you said
A: If you add the JS in your <head>, there should be no FOUC. There will always be a bit of FOUC with webpack-dev-server since the code is optimized for quick code changes, not render/loading speed. But, this new development may be the behavior you are looking for: https://github.com/rails/webpacker/issues/2062#issuecomment-490558992
B: If you intend for your app to be rendered server-side, you would need to seek help in other SSR-related issues in this repo like https://github.com/rails/webpacker/issues/2085 or https://github.com/rails/webpacker/issues/2074. (I don't use SSR)
I was able to prevent FOUC and enable HMR for stylesheets like this:
In Webpacker development.js file:
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
// Replace default MiniCssExtractPlugin loader config to enable HMR
// You might want to change "sass" to the loader that you actually use
// Default loaders are `css`, `sass`, `moduleCss` or `moduleSass`
environment.loaders.get('sass').use[0] = {
loader: MiniCssExtractPlugin.loader,
options: {
hmr: true,
},
};
// Remove [hash] from filename in the plugin so HMR is able to match the file to refresh
environment.plugins.get('MiniCssExtract').options.filename = 'css/[name].css';
In webpacker.yml, enable CSS extraction for the development env:
development:
extract_css: true
# ...
dev_server:
hmr: true
inline: true
# ...
Ensure you have <%= stylesheet_pack_tag "your_pack_name" %> somewhere in your <head> in development too.
Can this issue be closed ?
Most helpful comment
You probably want to set
extract_css: truein development that way CSS would be extracted out of JS into a separate bundle and you won't see flashing due to CSS in JS.