Improve first contentful paint in React

Today I took a look at Page Speed Insights, to check the performance of the website, and long story short: I needed to improve it.

Starting point: Score 49 😢

On desktop devices, I got a score of 97, but it was 49 on mobile devices:

Improving First Contentful Paint

Here is the data:

ParameterValue
First Contentful Paint6.1 s
Speed Index6.1 s
Largest Contentful Paint:7.5 s
Time to interactive6.3 s
Total Blocking Time210 ms
Cumulative Layout Shift0

Improving it: Score 58 💅

I wanted to improve the First Contentful Paint parameter, which is the time when the browser renders the first bit of content.

This is one of the keys parameters to improve, since attention spans are low these days, and users haven't much patience. Google estimates that the ideal range of this parameter is between 0 and 2 seconds.

At first, I've thought that the main page had a problem with a chunk of JavaScript, so I tried to simplify it by removing some functions and refactoring the component.

Then, I relaunched the test and I saw a 9 points improvement:

Improving First Contentful Paint

Here is the data:

ParameterValue
First Contentful Paint3.9 s
Speed Index4.3 s
Largest Contentful Paint:6.1 s
Time to interactive4.7 s
Total Blocking Time440 ms
Cumulative Layout Shift0

Lazy loading: Score 98 🚀

If you are developing with React and create-react-app, you can see that after building your application, Webpack bundles JavaScript into a chunk (same with CSS). To improve website performance, it's recommended to divide the JavaScript into smaller chunks.

React API has a function called lazy and a component called Suspense. When Webpack detects a lazy component, it will be loaded after the non-lazy ones.

So, for example, let's say that you have the following app:

jsx
import React   from 'react';
import Nav     from './Nav';
import Content from './Content';
import Footer  from './Footer';

const App = () => {
    
    return(
        <>
            <Nav/>
            <Content/>
            <Footer/>
        </>
    );
    
}

export default App;

After running npm run build, React will create a file such as main.chunk.js. Depending on the size of <Nav/>, <Content/> and <Footer/>, the file can be heavy.

In this example, the three components will load at once.

Imagine that <Nav/> and <Footer/> are light components; if you display them before the content is loaded, the user experience will improve (the user wants to see something on the screen as soon as possible).

jsx
import React, { Suspense, lazy } from 'react';
import Nav                       from './Nav';
import Footer                    from './Footer';

const Content = lazy(() => import('./Content'));

const App = () => {
        
    return(
        <>
            <Nav/>
            <Suspense fallback = {<div>Loading...</div>}>
                <Content/>
            </Suspense>
            <Footer/>
        </>
    );
    
}

export default App;

At this point, <Content/> will be suspended until it's loaded. As you can see, the <Suspense/> component has fallback as a prop, which is the HTML to show while the component is loading.

After building the application, you'll have small JavaScript chunks, such as 01.5664eb.chunk.js, 02.54678eg.chunk.js, etc.

So, now, if the user launches the application, the navigator and footer will display first, and the content a little bit later. Using lazy imports, improved the First Contentful Paint to 1.6 s and a score of 98:

Improving First Contentful Paint

Here is the data:

ParameterValue
First Contentful Paint1.6 s
Speed Index2.6 s
Largest Contentful Paint:2.2 s
Time to interactive1.6 s
Total Blocking Time0 ms
Cumulative Layout Shift0

Hi, I'm Erik, an engineer from Barcelona. If you like the post or have any comments, say hi.