How to Stop Fouc When Using CSS Loaded by Webpack

How to stop FOUC when using css loaded by webpack

ExtractTextWebpackPlugin will allow you to output your CSS as a separate file rather than having it embedded in your JS bundle. You can then include this file in your HTML, which as you said, prevents the flash of unstyled content.

I'd recommend only using this in production environments, as it stops hot-loading from working and makes your compile take longer. I have my webpack.config.js set up to only apply the plugin when process.env.NODE_ENV === "production"; you still get the FOUC when you're doing a development build/running the dev server, but I feel like this is a fair trade off.

For more information on how to set this up, take a look at SurviveJS's guide.


Update: As noted in the comments, ExtractTextWebpackPlugin has now been superceded by mini-css-extract-plugin - you should use that instead.

Struggling to remove FOUC (Flash Of Unstyled Content) when using Webpack

Right now your styles are baked into the JS files. In your case, the browser takes a while to parse the javascript and only after processing it can apply the styles to the page. That is causing the FOUC.

To cope with this problem ExtractTextPlugin was developed. Basically what it does is it takes out the css specified and puts it in a separate css file. A basic configuration would look like:

const plugin = new ExtractTextPlugin({
filename: '[name].[contenthash:8].css',
});

module: {
rules: [ {
test: /\.css$/,
use: plugin.extract({
use: 'css-loader',
fallback: 'style-loader',
})
}]
},
plugins: [ plugin ]

Then you must attach the generated file to your HTML page if you're not using html-webpack-plugin. By linking generated file in section you will get rid of FOUC.

Flash of unstyled content with react and scss

The problem wasn't because of Webpack. It was because of me being stupid :)

I imported the CSS file inside of a child component, so when I reload the page, it first shows the layout without any styles and then loads the child component and gives the layout a proper style. So basically, the only thing I did to fix the problem was to import the style in the layout component.

import '../style.scsc'

How to resolve FOUC in React.js

FOUC

FOUC - so called Flash of Unstyled Content can be as very problematic as so many tries of solving this issue.

To the point

Let's consider following configuration of routing (react-router):

...
<PageLayout>
<Switch>
<Route exact path='/' component={Home} />
<Route exact path='/example' component={Example} />
<Switch>
</PageLayout>
...

where PageLayout is a simple hoc, containing div wrapper with page-layout class and returning it's children.

Now, let's focus on the component rendering based on route. Usually you would use as component prop a React Compoment. But in our case we need to get it dynamically, to apply feature which helps us to avoid FOUC. So our code will look like this:

import asyncRoute from './asyncRoute'

const Home = asyncRoute(() => import('./Home'))
const Example = asyncRoute(() => import('./Example'))

...

<PageLayout>
<Switch>
<Route exact path='/' component={Home} />
<Route exact path='/example' component={Example} />
<Switch>
</PageLayout>

...

to clarify let's also show how asyncRoute.js module looks like:

import React, { Component } from 'react'
import PropTypes from 'prop-types'

import Loader from 'components/Loader'

class AsyncImport extends Component {
static propTypes = {
load: PropTypes.func.isRequired,
children: PropTypes.node.isRequired
}

state = {
component: null
}

toggleFoucClass () {
const root = document.getElementById('react-app')
if (root.hasClass('fouc')) {
root.removeClass('fouc')
} else {
root.addClass('fouc')
}
}

componentWillMount () {
this.toggleFoucClass()
}

componentDidMount () {
this.props.load()
.then((component) => {
setTimeout(() => this.toggleFoucClass(), 0)
this.setState(() => ({
component: component.default
}))
})
}

render () {
return this.props.children(this.state.component)
}
}

const asyncRoute = (importFunc) =>
(props) => (
<AsyncImport load={importFunc}>
{(Component) => {
return Component === null
? <Loader loading />
: <Component {...props} />
}}
</AsyncImport>
)

export default asyncRoute

hasClass, addClass, removeClass are polyfills which operates on DOM class attribute.

Loader is a custom component which shows spinner.

Why setTimeout?

Just because we need to remove fouc class in the second tick. Otherwise it would happen in the same as rendering the Component. So it won't work.

As you can see in the AsyncImport component we modify react root container by adding fouc class. So HTML for clarity:

<html lang="en">
<head></head>
<body>
<div id="react-app"></div>
</body>
</html>

and another piece of puzzle:

#react-app.fouc
.page-layout *
visibility: hidden

sass to apply when importing of specific component (ie.: Home, Example) takes place.

Why not display: none?

Because we want to have all components which rely on parent width, height or any other css rule to be properly rendered.

How it works?

The main assumption was to hide all elements until compoment gets ready to show us rendered content. First it fires asyncRoute function which shows us Loader until Component mounts and renders. In the meantime in AsyncImport we switch visibility of content by using a class fouc on react root DOM element. When everything loads, it's time to show everything up, so we remove that class.

Hope that helps!

Thanks to

This article, which idea of dynamic import has been taken (I think) from react-loadable.

Source

https://turkus.github.io/2018/06/06/fouc-react/



Related Topics



Leave a reply



Submit