This has been giving me a real headache for the last few hours. I've been developing my shiny new Next.js website (the one you're looking at right now) using yarn dev
, and everything's been golden. But as I went to launch and test a production build I noticed a nasty FOUC (flash of unstyled content) on first load and, even worse, all of my CSS transitions would trigger at once, which looked ridiculous.
The problem seems to lie in the way CSS modules work - the style tags aren't server-side rendered into the page but are instead injected client side, so the page is rendered, then the CSS is injected, which causes the elements to immediately transition to their intended states (many of which were off-screen in my case).
When you Google this there are all sorts of fixes and workarounds out there, but none of them quite did the job for me, or were a bit too hacky for my liking. This was complicated by the fact that there a handful of issues which are similar but not quite the same. Here are a few links though in case you stumble across this in Google and want to try some other options:
- Stack Overflow - CSS Transition flash on page load with NextJS production build
- Github - Pages render without styles for a brief second
- Github - FOUC using Next.js 10
What did the job for me was automatically inlining critical CSS (i.e. sticking it in the head) so that it was available to the page on first render. There are many ways of doing this yourself, but fortunately Next.js offers experimental support for this behind a config parameter (as of the time of writing this article, date in the footer), so that's what I went with.
To do so, firstly add the following property to your next.config.js:
module.exports = {
experimental: {
optimizeCss: true,
...
},
...
}
And secondly, support for this requires a peer dependency of Critters, so add this to your project as well:
yarn add critters
// or `npm install critters` if you prefer
Job done! Now when you build
your project you should see some logs letting you know how much CSS has been inlined into each page - and this CSS will be present when you View Source, meaning the code has been sent from the server.
Naturally, the suitability of this for your project may vary, but for a relatively small project like this website it felt appropriate and solved the problem in what feels like the "correct" way given the cause of the issue.