Pablo
Pablo

Reputation: 10620

React check if css has loaded with Webpack and React

I'm developing a project in React with Webpack in ES2015.

I am importing the styles as

import '../assets/main.scss'

it works fine, but seems to take longer than the rest of the page, so for a second, everything looks very ugly.

is there a way I can check if the style has been loaded before start rendering the page?

Upvotes: 2

Views: 3543

Answers (1)

yadhu
yadhu

Reputation: 15642

"for a second, everything looks very ugly."

This phenomenon is called FOUC (Flash of unstyled content).

Solutions:

Since you are using webpack, you'd be using webpack loaders too. So the first level of optimization can be done in your webpack.config.js.

Use webpack's ExtractTextWebpackPlugin.

It moves every require("style.css") in entry chunks into a separate css output file. So your styles are no longer inlined into the javascript, but separate in a css bundle file (styles.css). If your total stylesheet volume is big, it will be faster because the stylesheet bundle is loaded in parallel to the javascript bundle.

The text in bold is our first key to kill FOUC.

example:

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

module.exports = {
    module: {
        loaders: [
            // Extract CSS during build
            {
                test: /\.css$/,
                loader: ExtractTextPlugin.extract('style', 'css')
            }
        ]
    },
    plugins: [
        // Output extracted CSS to a file
        new ExtractTextPlugin('[name].[chunkhash].css')
    ]
}

You can tweak this further to use different loaders for .scss or .less.

The ExtractTextPlugin generates an output file per entry, so you must use [name], [id] or [contenthash] when using multiple entries.

Create multiple instances of ExtractTextWebpackPlugin if your want different bundles for your different loader patterns (as described in the last section of the API doc).

So what we achieve ?

  • Separates styling from JavaScript neatly
  • Fewer style tags (older IE has a limit)
  • CSS SourceMap (with devtool: "source-map" and css-loader?sourceMap)
  • Load CSS in parallel to JS (doesn't have to wait for JavaScript to load to get styling information)
  • Separate caching of our CSS files and smaller JS files.
  • Faster runtime (less code and DOM operations)

Nota Bene: There are some caveats for using ExtractTextWebpackPlugin which are also described in the original documentation page.

Separate CSS bundle and Webpack's Code Splitting [official doc]

With Code Splitting we can use two different modes:

  • Create one css file per initial chunk (see Code Splitting) and embed stylesheets into additional chunks. (recommended)
  • Create one css file per initial chunk which also contains styles from additional chunks.

For big web apps it’s not efficient to put all code into a single file, especially if some blocks of code are only required under some circumstances. Webpack has a feature to split your codebase into “chunks” which are loaded on demand.

Code splitting can be done Using webpack's require.ensure by defining multiple split. This is pretty easy to configure see official doc, or my own SO answer for a React.js-React-Router-Webpack application ;)

There was an an issue reported when async-injection of css splits ExtractTextWebpackPlugin and require.ensure are used together, and here is the unofficial solution for that (use file-loader and extract-loader combined):

{
     test: /\.css$/,
     loaders: ["style-loader/url","file?name=app/[name].[hash].css!extract","css-loader","postcss-loader"]
}

Needs more improvement?

Once we have configured ExtractTextWebpackPlugin, further optimization of CSS can be done using PurifyCSS WebPack Plugin.

This is a plugin for WebPack that utilizes PurifyCSS to clean your CSS and removes unused code.


"Is there a way I can check if the style has been loaded before start rendering the page?"

Here is a list of reference links explaining some general techniques to minimize FOUC:

Upvotes: 8

Related Questions