/ FOUC

Fixing Flash of Unstyled Content (FOUC) in your Webpack 4 Build

The Issue

When I first deployed my Crypto Coaster project to production, I was serving the page's assets straight up from my Node/Express app. Most modern web pages use some type of bundler for numerous reasons which you can read aboue here. When I finally migrated the app to using a modern bundler, Webpack 4, I had run into an issue where the page would have a flash of unstyled content (FOUC). I hadn't run into this issue from serving the assets straight up and wanted to get rid of FOUC.

My initial webpack.config.js file looked like this:

const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');

let config = {
	entry: './src/js/app.js',
	output: {
		filename: './bundle.js'
	},
	devServer: {
		contentBase: './dist'
	},
	module: {
		rules: [
			{
				test: /\.js$/,
				exclude: /(node_modules)/,
				use: {
					loader: 'babel-loader',
					options: {
						presets: ['@babel/preset-env']
					}
				}
			},
			{
				test: /\.(gif|png|jpe?g|svg)$/i,
				use: [
				  'file-loader',
				]
			},
			{
				test: /\.scss$/,
				use: [{
						loader: "style-loader"
				}, {
						loader: "css-loader"
				}, {
						loader: "sass-loader"
				}]
			}
		]
	},
	plugins: [
        new HtmlWebpackPlugin({
          template: 'src/index.html'
        })
	]
}

module.exports = config;

I want to focus on the style loading section of the config (as that is where the issue was arising). I was using 3 different loaders to bundle the .css of the project:

  1. sass-loader - compiles .scss files into .css
  2. css-loader - reads in css files as a string
  3. style-loader - takes the css string from the css-loader and creates a <style> tag in the page's <head> with those styles.

This was all well and good, the project would get bundled correctly. However, on load, the following page would flash for under a second:

Screen-Shot-2018-04-09-at-8.24.11-pm

The reason why this config setup caused a FOUC was because of the style-loader. As the loader inlines all the css style within the bundle, the browser has to first parse the javascript and only after processing it can apply the styles to the page. This causes the FOUC.

The Soution

Explanation

The fix to the FOUC was to include a Webpack Plugin called Extract Text Loader. This plugin basically extracts all the css that is required in the bundle but instead of inlining it (causing the FOUC), creates a separate css file. By simply including this output css file, the css loads in parallel result in no FOUC!

Config Updates

I added the plugin to my dev dependencies for Webpack 4:

npm i -D extract-text-webpack-plugin@next

(Please note, as of 9/4/18 the @next versioning is required. This makes the plugin compatible with Webpack 4). And then included it within webpack.config.js:

const ExtractTextPlugin = require("extract-text-webpack-plugin");.

I then had to update the style config section from:

{
    test: /\.scss$/,
    use: [{
            loader: "style-loader"
    }, {
            loader: "css-loader"
    }, {
            loader: "sass-loader"
    }]
}

To:

{
    test: /\.scss$/,
    use: ExtractTextPlugin.extract({
        use: [ 
            'css-loader',
            'sass-loader'
        ]
    })
}

What basically changed was two things:

  1. Place the loaders within the ExtractTextPlugin
  2. Removal of the style-loader (as that inlines the css into your bundle.js)

The final step was to add the ExtractTextPlugin to the plugin section of the config:

plugins: [
    new HtmlWebpackPlugin({
        template: 'src/index.html',
        filename: 'index.html',
    }),
    new ExtractTextPlugin("styles.css")
]

This will instruct ExtractTextPlugin to output the css with the name styles.css in our dist directory.

I have also included the HtmlWebpackPlugin. This plugin works great with ExtractTextPlugin! After placing your require in your main js file (app.js) in my case: require('../css/styles.scss');, it will automatically detect the generated styles.css file is required and automatically add a reference to it in the generated index.html.

If you don't want to use the HtmlWebpackPlugin, simply just add a reference to the output styles.css.

Conclusion

After the above changes, the app now loads as expected with no FOUC and has reduced the amount of data required to load my application!