We are building our new website entirely in React and utilizing code-splitting & scss. Whenever a new page is requested it loads the raw HTML in the browser first and then a split second or so later the css styling comes in, seems to be a FOUC issue. This makes for a terrible experience and we need to figure out how to ensure the CSS is loaded before rendering the component(s). Does anyone have any experience with this? There seems to be a lack of information online currently with this issue. We currently have 10 js chunks but only one main.XXXXXXX.css.
-
Are you using a bundler , such as webpack? – Andrew Mar 26 '18 at 18:19
-
Thanks for the reply Andrew, I am using create-react-app so their bundled webpack/babel setup and I would really prefer to not eject if possible – Gregg Mar 26 '18 at 18:44
-
1Have you tried using the production build via `npm run build`? It will process the CSS and and save it to `build/static/css/main.xxxx.css`. This file can be cached (if the web server is setup correctly) so the browser should be able to render it without the unsightly delay. See: https://survivejs.com/webpack/styling/separating-css/ – Jeremy Mar 26 '18 at 20:56
-
1Thanks Jeremy, at the end of my question I stated we currently have the one main.xxxx.css file already. Our pipeline will auto deploy the optimized production build on each push to our respective branches. I didn't have this issue on previous version of react, but we weren't code splitting. I'm wondering if it's a code splitting issue? – Gregg Mar 26 '18 at 21:13
2 Answers
If you only ever have a single main.XXXXXXX.css file, then you should manually inject it into the <head>
section of the initial html view and have the entry to your app (aka the initial JS file or some webpack manifest JS file) loaded at the bottom of the same html view.
For example
<html>
<head>
...some header stuff
<link type="text/css" rel="stylesheet" href="/path/to/main.XXXXXXX.css">
</head>
<body>
<div id="react-app"></div>
<script src="/path/to/vendor.js"></script>
<script src="/path/to/app_entry_or_webpack_manifest.js"></script>
<body>
</html>
I am assuming you have a server that is serving up at least a page that looks similar to the above. In this case (above), your CSS will load before your JS and you should avoid the FOUC issue.
This problem becomes much more difficult if you have many css files, dynamically generated (via code splitting) as you break your app apart, but it doesn't sound like you have that problem yet.
Edit: Just realized that you may not know how to inject a dynamically created css sheet into your entry html file. If you use mini-css-extract-plugin then you can specify the filename it produces. In this example, you can see you can optionally include a [hash] or not. You prolly don't want the hash.
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const devMode = process.env.NODE_ENV !== 'production'
module.exports = {
plugins: [
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
// both options are optional
filename: devMode ? '[name].css' : '[name].[hash].css',
chunkFilename: devMode ? '[id].css' : '[id].[hash].css',
})
],
module: {
rules: [
{
test: /\.(sa|sc|c)ss$/,
use: [
devMode ? 'style-loader' : MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
'sass-loader',
],
}
]
}
}

- 472
- 3
- 10
-
2Having styles at `` doesn't guarantee that they'll have been parsed before ` – zurfyx Oct 21 '18 at 15:54
-
Most browser's will render (paint) once they have a "render tree" created and the "render tree" contains the CSSOM (aka all the styling information). Once a browser encounters a call to fetch a stylesheet, it now needs this information to successfully create it's "render tree" and to start painting the page. – Timothy Beamish Nov 18 '19 at 00:02
-
Before the styles have downloaded, a browser will attempt to parse the rest of the HTML, including script tags, thus creating its "content tree", but it shouldn't start painting until it has its "render tree" completed. Scripts also halt the creation of the "content tree" so it's true that fetching the script will take priority. But painting won't happen until that stylesheet has been parsed. Here's a related thread: https://stackoverflow.com/questions/34269416/when-does-parsing-html-dom-tree-happen – Timothy Beamish Nov 18 '19 at 00:06
-
Hi @TimothyBeamish, I do have a complicated setup like you explained. Code splitting with react-loadable and SSR. It seems like some of the css chunks are injected once the app is initialized, which takes a second. Meanwhile you see FOUC. Wondering if you have ever came across this issue. Trying to find a solution without going on embedding critical css path. TIA. – Harshana Oct 14 '20 at 13:20
-
@HarshanaAbeyaratne - we've since switched over to the CSS-in-JS pattern and are using the Emotion library to do this. It's made life a lot easier, cleaner and more consistent. – Timothy Beamish Nov 27 '20 at 21:34
-
If this is not a path for you, then you have to find a way to extract all CSS out and load it before the app loads. The best way to consistently do that is to extract it into a single CSS file and if not, then to have a very tight naming convention for the various CSS files that are generated and then find a way to preload them (knowing their name before-hand). I don't know the specifics of how to achieve this but that's the general strategy. – Timothy Beamish Nov 27 '20 at 21:34
I can think of two options:
- Call the css inside the index.js of your react app.
- Render the react app after the DOM is loaded. Try adding something like this in your index.js react file:
document.addEventListener("DOMContentLoaded", function(event) {
ReactDOM.render(
<App />,
document.getElementById('root')
);
});
I consider that the best practice is that the HTML styles rendered by a ReactJS component should be called in that component. So I prefer option 1. Note: if the css file is a global file, you can consider a refactor to have only the styles of the component inside the component.

- 1,484
- 1
- 7
- 20
-
-
I think the CSS is too heavy, so even though it is called before in the waterfall, it takes longer to load than the script. – Nico Diz May 24 '19 at 17:38
-