What the FOUC is happening: Flash of Unstyled Content

What the FOUC is happening: Flash of Unstyled Content


6 min read

Recently while I was browsing Instagram stories on my phone, a certain ad by Kopidate caught my eye. Despite having 'date' as part of its name, I thought it was interesting that they say it is not a matchmaking site but for humanising 1:1 connections. The "human" part was something that I felt resonating. So I clicked on it to visit the site, but oh boy, it was yet another website that is plagued by the FOUC problem. Observe the difference in styling from when the page just loaded and when the page finishes loading.


This phenomenon is known as Flash of Unstyled Content (FOUC), and it is a topic covered by a question included in the hp5b Frontend Interviewer Questions.

What is Flash of Unstyled Content?

FOUC is the short moment of truth when you catch your friend turning their head without the zoom filter applied - and often, it is aesthetically less appealing, just like the example above before the transition happened. FOUC poses a very bad user experience, especially for new visitors who are not aware of this phenomenon. They might even think that your website poses a security threat (my mom asks me that a lot for new sites she visit). Even for users who know of this phenomenon, by its existence, the lag and abruptness of the transition decreases the legitimacy and professionalism of a website.

Why does the Flash of Unstyled Content happen?

Dave Hyatt, an author from Webkit, the open-sourced web browser engine used by Safari sheds the light on the FOUC problem in great detail in this article.

In particular, the "general how":

This situation occurs whenever a Web browser ends up showing your Web page’s content without having any style information yet.

and the "exactly how":

when/how a browser ends up committing the crime of FOUCing depends heavily on how the browser’s engine is architected and on interesting assumptions made by Web site authors when designing their sites.

The rest of his article focuses on how to mitigate FOUC specifically for browsers that implement web engines, and for the typical web developer, accounting for how the web browser engine works is usually out of their usual scope of work, so we will not discuss that here.

Instead, let's see if we can dig deeper into the latter part which was previously highlighted by the author to avoid FOUC - assumptions made by web site authors when designing their sites.

Revisiting the Case Study

For emulating what I saw for Kopidate on my mobile but on my laptop's browser, at the Google Chrome Inspector Network Tab:

  • Changed the network to Fast 3G mode
  • Disable cache
  • Start recording the process
  • Observe the logs

You can try it out too 👇


In the .GIF animation below, you can see that it took roughly 11s for Fast 3G mode to load finish. The transition felt very laggy and the time it took to load is simply painful for many mobile users.


So what is the device's browser doing in that 11s? A few observations as a normal user:

  • text styles changing gradually
  • brand image appearing
  • text and button being moved down
  • a big flash of red background image and changing of the entire style shown in viewport

And if you're playing around the network logs, you would then roughly understand what is causing the above to happen. For those that are not familiar with the network inspector, although the names of the files that are being retrieved are some randomized junk and not meant to be understood, we can still see the type of the data that is retrieved e.g. base64 is usually image. Then we can click such files to see which image(s) are exactly being retrieved.

Interestingly, it was spending the majority of the first 2s - 10s fetching and loading a lot of other images that the user cannot see beyond their **device viewport. It was only in the last second before it finally fetched the red background portion and show the intended layout of styles correctly in the area that the user's device viewport covers. This probably indicates the presence of some css mask property where the text will appear differently based on the background image, as the web designer probably assumed the background image is always there and didn't create a fallback for the text styling in the absence of the background image.

For reference, I was previously using 5G data network to see that the abrupt transition on my phone happening in 1s. So that means that depending on the network speed, the user's experience will be different as well. In summary, the negligence for both the FOUC problem and performance would very likely result in a major turn off of web applications, possibly turning away many potential users.

Now the finale: how to FOUC off!

For vanilla websites that only uses HTML, CSS with no shiny framework

At the start of the .html file, put a style for hiding your site

<!doctype html>
    <style>html{visibility: hidden;opacity:0;}</style>
        <link rel="stylesheet" href="style1.css" />
        <link rel="stylesheet" href="style2.css">
        <link rel="stylesheet" href="style3.css">

At the end of the last css file to be loaded i.e. style3.css

html {
    visibility: visible;
    opacity: 1;

✅ The above guarantees that your html does not show until all the css files have been loaded. So there will not be a shitty transition happening.

✅ This is also a recommended solution for supporting devices/browsers that don't have Javascript enabled.

❌ If your website that has a lot of static content, even your fast 3G users will still be staring at a blank screen for quite some time. Do consider integrating with browser level image lazy-loading for a more holistic solution!

If you are using a shiny component framework like React, Vue, Angular etc

A simple fix is to do something similar to the solution provided above for vanilla websites, but using component state management.

An example with React without fallback 📘 What it does is that the side effect hook ensures that the component is mounted before rendering. And if it is mounted, that means that sequentially in the code, the import statement for the stylesheet would have been triggered before the code block for component, ensuring that it gets the stylesheet.

import './app.css';
import { useState }, React from 'react';

const defaultState = {
    loading: false

const AppWithoutFallback = () => {
    const [loading, setLoading] = useState(defaultState.loading);
    useEffect(() => { setLoading(false) }, []);

    return (
        <div className="app" style=style={{ visibility: this.state.loading? 'hidden' : 'loading' }}></div>

Another example for alternative fallback layout that is not dependent on CSS files. Of course for this you have to design it so that when it switches between the states, the transition doesn't look super abrupt.

import './app.css';
import { useState }, React from 'react';

const defaultState = {
    loading: false

const AppWithFallback = () => {
    const [loading, setLoading] = useState(defaultState.loading);
    useEffect(() => { setLoading(false) }, []);

    return (
            ? <div>Fallback content</div>
            : <div className="app-container"></div>

And again, for the above, you can incorporate the code with some lazy loading libraries too to improve performance - these will be provided in the resources section later.


Thank you for reading this article!

If you enjoyed reading, please leave some reactions 💌

I will be very happy to receive any feedback from you too 🌻

More resources

If you find this article to be too short, feel free to browse through the resources that I've looked at below too.

Did you find this article valuable?

Support Estee Tey by becoming a sponsor. Any amount is appreciated!